diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt index 1bbdc84d7635..764275f92046 100644 --- a/docs/SyntaxSummary.txt +++ b/docs/SyntaxSummary.txt @@ -75,11 +75,12 @@ grammar. {\small \begin{lstlisting} - Literal ::= [`-'] integerLiteral + SimpleLiteral ::= [`-'] integerLiteral | [`-'] floatingPointLiteral | booleanLiteral | characterLiteral | stringLiteral + Literal ::= SimpleLiteral | processedStringLiteral | symbolLiteral | `null' @@ -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} diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 0ab68ce2921e..0dde5de95f88 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -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 diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 0e9f5d9b217a..5fd3b81d0b32 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -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 } diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index d6125f236b31..69d7e5233794 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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 @@ -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) diff --git a/src/dotty/tools/dotc/parsing/Tokens.scala b/src/dotty/tools/dotc/parsing/Tokens.scala index 226a3710db2b..190226635c4e 100644 --- a/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/src/dotty/tools/dotc/parsing/Tokens.scala @@ -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) diff --git a/src/dotty/tools/dotc/transform/Literalize.scala b/src/dotty/tools/dotc/transform/Literalize.scala index 4a223e912984..f33baa52b810 100644 --- a/src/dotty/tools/dotc/transform/Literalize.scala +++ b/src/dotty/tools/dotc/transform/Literalize.scala @@ -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 = diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index f6a0c8cc3b95..0e8b4d8cf1a6 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -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) { diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b39d0e928951..b5af92d64c44 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -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) diff --git a/tests/neg/singletons.scala b/tests/neg/singletons.scala new file mode 100644 index 000000000000..5dff13096f58 --- /dev/null +++ b/tests/neg/singletons.scala @@ -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 +} diff --git a/tests/pos/singletons.scala b/tests/pos/singletons.scala new file mode 100644 index 000000000000..4ce41a0619a9 --- /dev/null +++ b/tests/pos/singletons.scala @@ -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 { + final lazy module val Test: Test$ = new Test$() + final module class Test$() extends Object() { this: => + def x(): Int = 1 + final def y(): Int = 1 + def z(): Int = 1 + final lazy module val O: Test.O$ = new Test.O$() + final module class O$() extends Object() { this: => + final def x(): Int = 42 + } + def fourtyTwo(): Int = 42 + final def a(): Int = { + println("x") + 2 + } + def b(): Int = 2 + def f(): Int = 3 + final def c(): Int = Test.f() + def dc(): Double = 3.0 + final def dc1(): Double = 3.0 + def fc(): Float = 3.0 + final def fc1(): Float = 3.0 + def t(): Boolean = true + def str(): String = "" + final def str2(): String = "" + } +} +*/