Skip to content

Commit b779f16

Browse files
committed
Merge pull request #771 from dotty-staging/fix-final-vals
Fix final vals
2 parents de9877f + 739f8ea commit b779f16

File tree

10 files changed

+96
-11
lines changed

10 files changed

+96
-11
lines changed

docs/SyntaxSummary.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,12 @@ grammar.
7575

7676
{\small
7777
\begin{lstlisting}
78-
Literal ::= [`-'] integerLiteral
78+
SimpleLiteral ::= [`-'] integerLiteral
7979
| [`-'] floatingPointLiteral
8080
| booleanLiteral
8181
| characterLiteral
8282
| stringLiteral
83+
Literal ::= SimpleLiteral
8384
| processedStringLiteral
8485
| symbolLiteral
8586
| `null'
@@ -108,6 +109,7 @@ grammar.
108109
| Path `.' `type' SingletonTypeTree(p)
109110
| `(' ArgTypes ')' Tuple(ts)
110111
| Refinement RefinedTypeTree(EmptyTree, refinement)
112+
| SimpleLiteral SingletonTypeTree(l)
111113
ArgType ::= Type
112114
| `_' TypeBounds
113115
ArgTypes ::= ArgType {`,' ArgType}

src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ object Flags {
437437

438438
/** Flags guaranteed to be set upon symbol creation */
439439
final val FromStartFlags =
440-
AccessFlags | Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
440+
AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
441441
InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed |
442442
CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent |
443443
SelfNameOrImplClass

src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,8 @@ object Types {
9494
/** Does this type denote a stable reference (i.e. singleton type)? */
9595
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
9696
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
97-
case _: SingletonType => true
97+
case _: SingletonType | NoPrefix => true
9898
case tp: RefinedType => tp.parent.isStable
99-
case NoPrefix => true
10099
case _ => false
101100
}
102101

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ object Parsers {
117117

118118
def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT
119119
def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name
120+
def isSimpleLiteral = simpleLiteralTokens contains in.token
120121
def isLiteral = literalTokens contains in.token
121122
def isNumericLit = numericLitTokens contains in.token
122123
def isModifier = modifierTokens contains in.token
@@ -709,12 +710,14 @@ object Parsers {
709710
* | Path `.' type
710711
* | `(' ArgTypes `)'
711712
* | Refinement
713+
* | Literal
712714
*/
713715
def simpleType(): Tree = simpleTypeRest {
714716
if (in.token == LPAREN)
715717
atPos(in.offset) { makeTupleOrParens(inParens(argTypes())) }
716718
else if (in.token == LBRACE)
717719
atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
720+
else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
718721
else path(thisOK = false, handleSingletonType) match {
719722
case r @ SingletonTypeTree(_) => r
720723
case r => convertToTypeId(r)

src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ object Tokens extends TokensCommon {
195195

196196
final val allTokens = tokenRange(minToken, maxToken)
197197

198-
final val literalTokens = tokenRange(CHARLIT, SYMBOLLIT) | BitSet(TRUE, FALSE, NULL)
198+
final val simpleLiteralTokens = tokenRange(CHARLIT, STRINGLIT) | BitSet(TRUE, FALSE)
199+
final val literalTokens = simpleLiteralTokens | BitSet(INTERPOLATIONID, SYMBOLLIT, NULL)
199200

200201
final val atomicExprTokens = literalTokens | identifierTokens | BitSet(
201202
USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, XMLSTART)

src/dotty/tools/dotc/transform/Literalize.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,13 @@ class Literalize extends MiniPhaseTransform { thisTransform =>
5151
* Revisit this issue once we have implemented `inline`. Then we can demand
5252
* purity of the prefix unless the selection goes to an inline val.
5353
*/
54-
def literalize(tree: Tree)(implicit ctx: Context): Tree = tree.tpe match {
55-
case ConstantType(value) if isIdempotentExpr(tree) => Literal(value)
56-
case _ => tree
54+
def literalize(tree: Tree)(implicit ctx: Context): Tree = {
55+
def recur(tp: Type): Tree = tp match {
56+
case ConstantType(value) if isIdempotentExpr(tree) => Literal(value)
57+
case tp: TermRef if tp.symbol.isStable => recur(tp.info.widenExpr)
58+
case _ => tree
59+
}
60+
recur(tree.tpe)
5761
}
5862

5963
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -706,10 +706,18 @@ class Namer { typer: Typer =>
706706

707707
// println(s"final inherited for $sym: ${inherited.toString}") !!!
708708
// println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
709+
def isInline = sym.is(Final, butNot = Method)
710+
def widenRhs(tp: Type): Type = tp match {
711+
case tp: TermRef => widenRhs(tp.underlying)
712+
case tp: ExprType => widenRhs(tp.resultType)
713+
case tp: ConstantType if isInline => tp
714+
case _ => tp.widen.approximateUnion
715+
}
709716
val rhsCtx = ctx.addMode(Mode.InferringReturnType)
710-
def rhsType = ctx.deskolemize(
711-
typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion)
712-
def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos)
717+
def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe
718+
def cookedRhsType = ctx.deskolemize(widenRhs(rhsType))
719+
def lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.pos)
720+
//if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType")
713721
if (inherited.exists) inherited
714722
else {
715723
if (sym is Implicit) {

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class tests extends CompilerTest {
146146
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
147147
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6)
148148
@Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 4)
149+
@Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 5)
149150
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
150151
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
151152
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)

tests/neg/singletons.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Test {
2+
val a: 42 = 43 // error: different constant
3+
val x = 42
4+
val z: 42 = x // error: x is not final
5+
6+
val n: null = null // error: Null is not a legal singleton type
7+
8+
val sym: 'sym = 'sym // error: Symbol is a legal singleton type
9+
10+
val foo: s"abc" = "abc" // error: not a legal singleton type
11+
}

tests/pos/singletons.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
object Test {
3+
4+
val x: 1 = 1
5+
final val y = x
6+
val z: 1 = y
7+
8+
object O { final val x = 42 }
9+
val fourtyTwo: 42 = O.x
10+
11+
final val a = { println("x"); 2 } // side effects don't matter
12+
val b: 2 = a
13+
14+
def f: 3 = 3
15+
final val c = f
16+
17+
val dc: 3.0 = 3.0
18+
final val dc1 = dc
19+
val fc: 3.0f = 3.0f
20+
final val fc1 = fc
21+
22+
val t: true = true
23+
24+
val str: "" = ""
25+
final val str2 = str
26+
}
27+
/* To do: test that after erasure we have generated code like this:
28+
*
29+
package <empty> {
30+
final lazy module val Test: Test$ = new Test$()
31+
final module class Test$() extends Object() { this: <notype> =>
32+
<accessor> def x(): Int = 1
33+
final <accessor> def y(): Int = 1
34+
<accessor> def z(): Int = 1
35+
final lazy module val O: Test.O$ = new Test.O$()
36+
final module class O$() extends Object() { this: <notype> =>
37+
final <accessor> def x(): Int = 42
38+
}
39+
<accessor> def fourtyTwo(): Int = 42
40+
final <accessor> def a(): Int = {
41+
println("x")
42+
2
43+
}
44+
<accessor> def b(): Int = 2
45+
def f(): Int = 3
46+
final <accessor> def c(): Int = Test.f()
47+
<accessor> def dc(): Double = 3.0
48+
final <accessor> def dc1(): Double = 3.0
49+
<accessor> def fc(): Float = 3.0
50+
final <accessor> def fc1(): Float = 3.0
51+
<accessor> def t(): Boolean = true
52+
<accessor> def str(): String = ""
53+
final <accessor> def str2(): String = ""
54+
}
55+
}
56+
*/

0 commit comments

Comments
 (0)