Skip to content

Commit d6e67d4

Browse files
authored
Merge pull request #5479 from dotty-staging/fix-#5386
Fix #5386: Normalize unary operator expressions
2 parents 69ef081 + 6701236 commit d6e67d4

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+18-2
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
449449
def isIdempotentRef(tree: Tree)(implicit ctx: Context): Boolean =
450450
refPurity(tree) >= Idempotent
451451

452-
/** If `tree` is a constant expression, its value as a Literal,
452+
/** (1) If `tree` is a constant expression, its value as a Literal,
453453
* or `tree` itself otherwise.
454454
*
455455
* Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose.
@@ -485,11 +485,27 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
485485
* Ident
486486
* Select
487487
* TypeApply
488+
*
489+
* (2) A primitive unary operator expression `pre.op` where `op` is one of `+`, `-`, `~`, `!`
490+
* that has a constant type `ConstantType(v)` but that is not a constant expression
491+
* (i.e. `pre` has side-effects) is translated to
492+
*
493+
* { pre; v }
494+
*
495+
* This avoids the situation where we have a Select node that does not have a symbol.
488496
*/
489497
def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
490498
val tree1 = ConstFold(tree)
491499
tree1.tpe.widenTermRefExpr match {
492-
case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value)
500+
case ConstantType(value) =>
501+
if (isIdempotentExpr(tree1)) Literal(value)
502+
else tree1 match {
503+
case Select(qual, _) if tree1.tpe.isInstanceOf[ConstantType] =>
504+
// it's a primitive unary operator; Simplify `pre.op` to `{ pre; v }` where `v` is the value of `pre.op`
505+
Block(qual :: Nil, Literal(value))
506+
case _ =>
507+
tree1
508+
}
493509
case _ => tree1
494510
}
495511
}

tests/run/i5386.check

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
!
2+
!
3+
!
4+
!
5+
1
6+
2
7+
!
8+
1

tests/run/i5386.scala

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
object Test extends App {
2+
3+
~{
4+
println("!")
5+
1
6+
}
7+
8+
+{
9+
println("!")
10+
1
11+
}
12+
13+
-{
14+
println("!")
15+
1
16+
}
17+
18+
!{
19+
println("!")
20+
true
21+
}
22+
23+
{ println("1"); 1 } + { println("2"); 2}
24+
25+
!(try true finally{()})
26+
27+
28+
class C {
29+
def foo: 1 = { println("1"); 1 }
30+
}
31+
32+
{ println("!"); new C }.foo
33+
34+
}

0 commit comments

Comments
 (0)