Skip to content

Fix final vals #771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 24, 2015
4 changes: 3 additions & 1 deletion docs/SyntaxSummary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ grammar.

{\small
\begin{lstlisting}
Literal ::= [`-'] integerLiteral
SimpleLiteral ::= [`-'] integerLiteral
| [`-'] floatingPointLiteral
| booleanLiteral
| characterLiteral
| stringLiteral
Literal ::= SimpleLiteral
| processedStringLiteral
| symbolLiteral
| `null'
Expand Down Expand Up @@ -108,6 +109,7 @@ grammar.
| Path `.' `type' SingletonTypeTree(p)
| `(' ArgTypes ')' Tuple(ts)
| Refinement RefinedTypeTree(EmptyTree, refinement)
| SimpleLiteral SingletonTypeTree(l)
ArgType ::= Type
| `_' TypeBounds
ArgTypes ::= ArgType {`,' ArgType}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ object Flags {

/** Flags guaranteed to be set upon symbol creation */
final val FromStartFlags =
AccessFlags | Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed |
CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent |
SelfNameOrImplClass
Expand Down
3 changes: 1 addition & 2 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,8 @@ object Types {
/** Does this type denote a stable reference (i.e. singleton type)? */
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
case _: SingletonType => true
case _: SingletonType | NoPrefix => true
case tp: RefinedType => tp.parent.isStable
case NoPrefix => true
case _ => false
}

Expand Down
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ object Parsers {

def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT
def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name
def isSimpleLiteral = simpleLiteralTokens contains in.token
def isLiteral = literalTokens contains in.token
def isNumericLit = numericLitTokens contains in.token
def isModifier = modifierTokens contains in.token
Expand Down Expand Up @@ -709,12 +710,14 @@ object Parsers {
* | Path `.' type
* | `(' ArgTypes `)'
* | Refinement
* | Literal
*/
def simpleType(): Tree = simpleTypeRest {
if (in.token == LPAREN)
atPos(in.offset) { makeTupleOrParens(inParens(argTypes())) }
else if (in.token == LBRACE)
atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
else path(thisOK = false, handleSingletonType) match {
case r @ SingletonTypeTree(_) => r
case r => convertToTypeId(r)
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ object Tokens extends TokensCommon {

final val allTokens = tokenRange(minToken, maxToken)

final val literalTokens = tokenRange(CHARLIT, SYMBOLLIT) | BitSet(TRUE, FALSE, NULL)
final val simpleLiteralTokens = tokenRange(CHARLIT, STRINGLIT) | BitSet(TRUE, FALSE)
final val literalTokens = simpleLiteralTokens | BitSet(INTERPOLATIONID, SYMBOLLIT, NULL)

final val atomicExprTokens = literalTokens | identifierTokens | BitSet(
USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, XMLSTART)
Expand Down
10 changes: 7 additions & 3 deletions src/dotty/tools/dotc/transform/Literalize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ class Literalize extends MiniPhaseTransform { thisTransform =>
* Revisit this issue once we have implemented `inline`. Then we can demand
* purity of the prefix unless the selection goes to an inline val.
*/
def literalize(tree: Tree)(implicit ctx: Context): Tree = tree.tpe match {
case ConstantType(value) if isIdempotentExpr(tree) => Literal(value)
case _ => tree
def literalize(tree: Tree)(implicit ctx: Context): Tree = {
def recur(tp: Type): Tree = tp match {
case ConstantType(value) if isIdempotentExpr(tree) => Literal(value)
case tp: TermRef if tp.symbol.isStable => recur(tp.info.widenExpr)
case _ => tree
}
recur(tree.tpe)
}

override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
Expand Down
14 changes: 11 additions & 3 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,18 @@ class Namer { typer: Typer =>

// println(s"final inherited for $sym: ${inherited.toString}") !!!
// println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
def isInline = sym.is(Final, butNot = Method)
def widenRhs(tp: Type): Type = tp match {
case tp: TermRef => widenRhs(tp.underlying)
case tp: ExprType => widenRhs(tp.resultType)
case tp: ConstantType if isInline => tp
case _ => tp.widen.approximateUnion
}
val rhsCtx = ctx.addMode(Mode.InferringReturnType)
def rhsType = ctx.deskolemize(
typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion)
def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos)
def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe
def cookedRhsType = ctx.deskolemize(widenRhs(rhsType))
def lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.pos)
//if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType")
if (inherited.exists) inherited
else {
if (sym is Implicit) {
Expand Down
1 change: 1 addition & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class tests extends CompilerTest {
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6)
@Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 4)
@Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 5)
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)
Expand Down
11 changes: 11 additions & 0 deletions tests/neg/singletons.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object Test {
val a: 42 = 43 // error: different constant
val x = 42
val z: 42 = x // error: x is not final

val n: null = null // error: Null is not a legal singleton type

val sym: 'sym = 'sym // error: Symbol is a legal singleton type

val foo: s"abc" = "abc" // error: not a legal singleton type
}
56 changes: 56 additions & 0 deletions tests/pos/singletons.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

object Test {

val x: 1 = 1
final val y = x
val z: 1 = y

object O { final val x = 42 }
val fourtyTwo: 42 = O.x

final val a = { println("x"); 2 } // side effects don't matter
val b: 2 = a

def f: 3 = 3
final val c = f

val dc: 3.0 = 3.0
final val dc1 = dc
val fc: 3.0f = 3.0f
final val fc1 = fc

val t: true = true

val str: "" = ""
final val str2 = str
}
/* To do: test that after erasure we have generated code like this:
*
package <empty> {
final lazy module val Test: Test$ = new Test$()
final module class Test$() extends Object() { this: <notype> =>
<accessor> def x(): Int = 1
final <accessor> def y(): Int = 1
<accessor> def z(): Int = 1
final lazy module val O: Test.O$ = new Test.O$()
final module class O$() extends Object() { this: <notype> =>
final <accessor> def x(): Int = 42
}
<accessor> def fourtyTwo(): Int = 42
final <accessor> def a(): Int = {
println("x")
2
}
<accessor> def b(): Int = 2
def f(): Int = 3
final <accessor> def c(): Int = Test.f()
<accessor> def dc(): Double = 3.0
final <accessor> def dc1(): Double = 3.0
<accessor> def fc(): Float = 3.0
final <accessor> def fc1(): Float = 3.0
<accessor> def t(): Boolean = true
<accessor> def str(): String = ""
final <accessor> def str2(): String = ""
}
}
*/