Skip to content

Commit 89a0845

Browse files
committed
Handle by-name arguments
1 parent 9960e5a commit 89a0845

File tree

2 files changed

+46
-28
lines changed

2 files changed

+46
-28
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Util._
1919
import scala.collection.mutable
2020

2121
class Semantic {
22+
import Semantic._
2223

2324
// ----- Domain definitions --------------------------------
2425

@@ -410,6 +411,23 @@ class Semantic {
410411
def eval(exprs: List[Tree], thisV: Value, klass: ClassSymbol)(using Context, Trace): List[Result] =
411412
exprs.map { expr => eval(expr, thisV, klass) }
412413

414+
/** Evaluate arguments of methods */
415+
def evalArgs(args: List[Arg], thisV: Value, klass: ClassSymbol)(using Context, Trace): List[Error] =
416+
val ress = args.map { arg =>
417+
val res =
418+
if arg.isByName then
419+
thisV match
420+
case obj: (ThisRef | Warm) =>
421+
val value = Fun(arg.tree, obj, klass)
422+
Result(value, Nil)
423+
case _ => ??? // impossible
424+
else
425+
eval(arg.tree, thisV, klass)
426+
427+
res.ensureHot("May only use initialized value as arguments", arg.tree)
428+
}
429+
ress.flatMap(_.errors)
430+
413431
/** Handles the evaluation of different expressions
414432
*
415433
* Note: Recursive call should go to `eval` instead of `cases`.
@@ -426,23 +444,15 @@ class Semantic {
426444

427445
case NewExpr(tref, New(tpt), ctor, argss) =>
428446
// check args
429-
val args = argss.flatten
430-
val ress = args.map { arg =>
431-
eval(arg, thisV, klass).ensureHot("May use initialized value as arguments", arg)
432-
}
433-
val errors = ress.flatMap(_.errors)
447+
val errors = evalArgs(argss.flatten, thisV, klass)
434448

435449
val cls = tref.classSymbol.asClass
436450
val res = outerValue(tref, thisV, klass, tpt)
437451
(res ++ errors).instantiate(cls, ctor, expr)
438452

439453
case Call(ref, argss) =>
440454
// check args
441-
val args = argss.flatten
442-
val ress = args.map { arg =>
443-
eval(arg, thisV, klass).ensureHot("May use initialized value as arguments", arg)
444-
}
445-
val errors = ress.flatMap(_.errors)
455+
val errors = evalArgs(argss.flatten, thisV, klass)
446456

447457
ref match
448458
case Select(supert: Super, _) =>
@@ -667,19 +677,11 @@ class Semantic {
667677
def initParent(parent: Tree) = parent match {
668678
case tree @ Block(stats, NewExpr(tref, New(tpt), ctor, argss)) =>
669679
eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
670-
argss.flatten.foreach { arg =>
671-
val res = eval(arg, thisV, klass)
672-
res.ensureHot("Argument must be an initialized value", arg)
673-
errorBuffer ++= res.errors
674-
}
680+
errorBuffer ++= evalArgs(argss.flatten, thisV, klass)
675681
superCall(tref, ctor, tree)
676682

677683
case tree @ NewExpr(tref, New(tpt), ctor, argss) =>
678-
argss.flatten.foreach { arg =>
679-
val res = eval(arg, thisV, klass)
680-
res.ensureHot("Argument must be an initialized value", arg)
681-
errorBuffer ++= res.errors
682-
}
684+
errorBuffer ++= evalArgs(argss.flatten, thisV, klass)
683685
superCall(tref, ctor, tree)
684686

685687
case _ =>
@@ -728,7 +730,7 @@ class Semantic {
728730
*
729731
* This is intended to avoid type soundness issues in Dotty.
730732
*/
731-
def checkTermUsage(tpt: Tree, thisV: Value, klass: ClassSymbol)(using Context, Trace): List[Error] =
733+
def checkTermUsage(tpt: Tree, thisV: Value, klass: ClassSymbol)(using Context, Trace): List[Error] =
732734
val buf = new mutable.ArrayBuffer[Error]
733735
val traverser = new TypeTraverser {
734736
def traverse(tp: Type): Unit = tp match {
@@ -741,19 +743,39 @@ class Semantic {
741743
traverser.traverse(tpt.tpe)
742744
buf.toList
743745

746+
}
747+
748+
object Semantic {
749+
744750
// ----- Utility methods and extractors --------------------------------
745751

746752
def typeRefOf(tp: Type)(using Context): TypeRef = tp.dealias.typeConstructor match {
747753
case tref: TypeRef => tref
748754
case hklambda: HKTypeLambda => typeRefOf(hklambda.resType)
749755
}
750756

757+
opaque type Arg = Tree | ByNameArg
758+
case class ByNameArg(tree: Tree)
759+
760+
extension (arg: Arg)
761+
def isByName = arg.isInstanceOf[ByNameArg]
762+
def tree: Tree = arg match
763+
case t: Tree => t
764+
case ByNameArg(t) => t
765+
751766
object Call {
752-
def unapply(tree: Tree)(using Context): Option[(Tree, List[List[Tree]])] =
767+
768+
def unapply(tree: Tree)(using Context): Option[(Tree, List[List[Arg]])] =
753769
tree match
754770
case Apply(fn, args) =>
771+
val argTps = fn.tpe.widen match
772+
case mt: MethodType => mt.paramInfos
773+
val normArgs: List[Arg] = args.zip(argTps).map {
774+
case (arg, _: ExprType) => ByNameArg(arg)
775+
case (arg, _) => arg
776+
}
755777
unapply(fn) match
756-
case Some((ref, args0)) => Some((ref, args0 :+ args))
778+
case Some((ref, args0)) => Some((ref, args0 :+ normArgs))
757779
case None => None
758780

759781
case TypeApply(fn, targs) =>
@@ -766,7 +788,7 @@ class Semantic {
766788
}
767789

768790
object NewExpr {
769-
def unapply(tree: Tree)(using Context): Option[(TypeRef, New, Symbol, List[List[Tree]])] =
791+
def unapply(tree: Tree)(using Context): Option[(TypeRef, New, Symbol, List[List[Arg]])] =
770792
tree match
771793
case Call(fn @ Select(newTree: New, init), argss) if init == nme.CONSTRUCTOR =>
772794
val tref = typeRefOf(newTree.tpe)

tests/init/neg/t3273.check

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error
33
| ^^^^^^^^^^^^^^^
44
| Promoting the value to fully-initialized is unsafe.
5-
| Calling trace:
6-
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
75
|
86
| The unsafe promotion may cause the following problem(s):
97
|
@@ -13,8 +11,6 @@
1311
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
1412
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1513
| Promoting the value to fully-initialized is unsafe.
16-
| Calling trace:
17-
| -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ]
1814
|
1915
| The unsafe promotion may cause the following problem(s):
2016
|

0 commit comments

Comments
 (0)