Skip to content

Commit 4fae88a

Browse files
authored
Merge pull request #3565 from dotty-staging/more-sip23
Progress towards full SIP-23 support: correct inference for scala.Singleton, support for symbol literals
2 parents d929fc9 + 3f61cb5 commit 4fae88a

File tree

18 files changed

+133
-47
lines changed

18 files changed

+133
-47
lines changed

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,9 +1029,7 @@ object desugar {
10291029

10301030
val desugared = tree match {
10311031
case SymbolLit(str) =>
1032-
Apply(
1033-
ref(defn.SymbolClass.companionModule.termRef),
1034-
Literal(Constant(str)) :: Nil)
1032+
Literal(Constant(scala.Symbol(str)))
10351033
case InterpolatedString(id, segments) =>
10361034
val strs = segments map {
10371035
case ts: Thicket => ts.trees.head

compiler/src/dotty/tools/dotc/core/Constants.scala

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,28 @@ object Constants {
2121
final val ClazzTag = 12
2222
// For supporting java enumerations inside java annotations (see ClassfileParser)
2323
final val EnumTag = 13
24+
final val ScalaSymbolTag = 14
2425

2526
case class Constant(value: Any) extends printing.Showable {
2627
import java.lang.Double.doubleToRawLongBits
2728
import java.lang.Float.floatToRawIntBits
2829

2930
val tag: Int = value match {
30-
case null => NullTag
31-
case x: Unit => UnitTag
32-
case x: Boolean => BooleanTag
33-
case x: Byte => ByteTag
34-
case x: Short => ShortTag
35-
case x: Int => IntTag
36-
case x: Long => LongTag
37-
case x: Float => FloatTag
38-
case x: Double => DoubleTag
39-
case x: String => StringTag
40-
case x: Char => CharTag
41-
case x: Type => ClazzTag
42-
case x: Symbol => EnumTag
43-
case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass)
31+
case null => NullTag
32+
case x: Unit => UnitTag
33+
case x: Boolean => BooleanTag
34+
case x: Byte => ByteTag
35+
case x: Short => ShortTag
36+
case x: Int => IntTag
37+
case x: Long => LongTag
38+
case x: Float => FloatTag
39+
case x: Double => DoubleTag
40+
case x: String => StringTag
41+
case x: Char => CharTag
42+
case x: Type => ClazzTag
43+
case x: Symbol => EnumTag
44+
case x: scala.Symbol => ScalaSymbolTag
45+
case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass)
4446
}
4547

4648
def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue
@@ -54,19 +56,20 @@ object Constants {
5456
def isAnyVal = UnitTag <= tag && tag <= DoubleTag
5557

5658
def tpe(implicit ctx: Context): Type = tag match {
57-
case UnitTag => defn.UnitType
58-
case BooleanTag => defn.BooleanType
59-
case ByteTag => defn.ByteType
60-
case ShortTag => defn.ShortType
61-
case CharTag => defn.CharType
62-
case IntTag => defn.IntType
63-
case LongTag => defn.LongType
64-
case FloatTag => defn.FloatType
65-
case DoubleTag => defn.DoubleType
66-
case StringTag => defn.StringType
67-
case NullTag => defn.NullType
68-
case ClazzTag => defn.ClassType(typeValue)
69-
case EnumTag => defn.EnumType(symbolValue)
59+
case UnitTag => defn.UnitType
60+
case BooleanTag => defn.BooleanType
61+
case ByteTag => defn.ByteType
62+
case ShortTag => defn.ShortType
63+
case CharTag => defn.CharType
64+
case IntTag => defn.IntType
65+
case LongTag => defn.LongType
66+
case FloatTag => defn.FloatType
67+
case DoubleTag => defn.DoubleType
68+
case StringTag => defn.StringType
69+
case NullTag => defn.NullType
70+
case ClazzTag => defn.ClassType(typeValue)
71+
case EnumTag => defn.EnumType(symbolValue)
72+
case ScalaSymbolTag => defn.ScalaSymbolType
7073
}
7174

7275
/** We need the equals method to take account of tags as well as values.
@@ -206,6 +209,7 @@ object Constants {
206209

207210
def typeValue: Type = value.asInstanceOf[Type]
208211
def symbolValue: Symbol = value.asInstanceOf[Symbol]
212+
def scalaSymbolValue: scala.Symbol = value.asInstanceOf[scala.Symbol]
209213

210214
/**
211215
* Consider two `NaN`s to be identical, despite non-equality

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,11 @@ trait ConstraintHandling {
271271
var inst = approximation(param, fromBelow).simplified
272272

273273
// Then, approximate by (1.) - (3.) and simplify as follows.
274-
// 1. If instance is from below and is a singleton type, yet
275-
// upper bound is not a singleton type, widen the instance.
276-
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound))
274+
// 1. If instance is from below and is a singleton type, yet upper bound is
275+
// not a singleton type or a reference to `scala.Singleton`, widen the
276+
// instance.
277+
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound)
278+
&& !upperBound.isRef(defn.SingletonClass))
277279
inst = inst.widen
278280

279281
// 2. If instance is from below and is a fully-defined union type, yet upper bound

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,12 @@ class Definitions {
545545
lazy val FunctionXXLType: TypeRef = ctx.requiredClassRef("scala.FunctionXXL")
546546
def FunctionXXLClass(implicit ctx: Context) = FunctionXXLType.symbol.asClass
547547

548-
lazy val SymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol")
549-
def SymbolClass(implicit ctx: Context) = SymbolType.symbol.asClass
548+
lazy val ScalaSymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol")
549+
def ScalaSymbolClass(implicit ctx: Context) = ScalaSymbolType.symbol.asClass
550+
def ScalaSymbolModule(implicit ctx: Context) = ScalaSymbolClass.companionModule
551+
lazy val ScalaSymbolModule_applyR = ScalaSymbolModule.requiredMethodRef(nme.apply, List(StringType))
552+
def ScalaSymbolModule_apply(implicit ctx: Context) = ScalaSymbolModule_applyR.symbol
553+
550554
lazy val DynamicType: TypeRef = ctx.requiredClassRef("scala.Dynamic")
551555
def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass
552556
lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option")

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ Standard-Section: "ASTs" TopLevelStat*
147147
NULLconst
148148
CLASSconst Type
149149
ENUMconst Path
150+
SYMBOLconst NameRef
150151
151152
Type = Path
152153
TYPEREFdirect sym_ASTRef
@@ -231,7 +232,7 @@ object TastyFormat {
231232

232233
final val header = Array(0x5C, 0xA1, 0xAB, 0x1F)
233234
val MajorVersion = 1
234-
val MinorVersion = 0
235+
val MinorVersion = 1
235236

236237
// Name tags
237238

@@ -315,6 +316,7 @@ object TastyFormat {
315316
final val STRINGconst = 77
316317
final val IMPORTED = 78
317318
final val RENAMED = 79
319+
final val SYMBOLconst = 80
318320

319321
// Cat. 3: tag AST
320322

@@ -575,6 +577,7 @@ object TastyFormat {
575577
case SUPER => "SUPER"
576578
case CLASSconst => "CLASSconst"
577579
case ENUMconst => "ENUMconst"
580+
case SYMBOLconst => "SYMBOLconst"
578581
case SINGLETONtpt => "SINGLETONtpt"
579582
case SUPERtype => "SUPERtype"
580583
case TYPEARGtype => "TYPEARGtype"

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ class TreePickler(pickler: TastyPickler) {
115115
case EnumTag =>
116116
writeByte(ENUMconst)
117117
pickleType(c.symbolValue.termRef)
118+
case ScalaSymbolTag =>
119+
writeByte(SYMBOLconst)
120+
pickleName(c.scalaSymbolValue.name.toTermName)
118121
}
119122

120123
def pickleType(tpe0: Type, richTypes: Boolean = false)(implicit ctx: Context): Unit = {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi
331331
ConstantType(Constant(readType()))
332332
case ENUMconst =>
333333
ConstantType(Constant(readTermRef().termSymbol))
334+
case SYMBOLconst =>
335+
ConstantType(Constant(scala.Symbol(readName().toString)))
334336
case BYNAMEtype =>
335337
ExprType(readType())
336338
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ object Tokens extends TokensCommon {
201201

202202
final val allTokens = tokenRange(minToken, maxToken)
203203

204-
final val simpleLiteralTokens = tokenRange(CHARLIT, STRINGLIT) | BitSet(TRUE, FALSE)
205-
final val literalTokens = simpleLiteralTokens | BitSet(INTERPOLATIONID, SYMBOLLIT, NULL)
204+
final val simpleLiteralTokens = tokenRange(CHARLIT, STRINGLIT) | BitSet(TRUE, FALSE, SYMBOLLIT)
205+
final val literalTokens = simpleLiteralTokens | BitSet(INTERPOLATIONID, NULL)
206206

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

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
463463
case CharTag => literalText(s"'${escapedChar(const.charValue)}'")
464464
case LongTag => literalText(const.longValue.toString + "L")
465465
case EnumTag => literalText(const.symbolValue.name.toString)
466+
case ScalaSymbolTag => literalText("'" + const.scalaSymbolValue.name.toString)
466467
case _ => literalText(String.valueOf(const.value))
467468
}
468469

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,17 @@ object Erasure {
342342
assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1)
343343
}
344344

345-
override def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Literal =
346-
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
347-
else if (tree.const.tag == Constants.ClazzTag) Literal(Constant(erasure(tree.const.typeValue)))
348-
else super.typedLiteral(tree)
345+
override def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Tree =
346+
if (tree.typeOpt.isRef(defn.UnitClass))
347+
tree.withType(tree.typeOpt)
348+
else if (tree.const.tag == Constants.ClazzTag)
349+
Literal(Constant(erasure(tree.const.typeValue)))
350+
else if (tree.const.tag == Constants.ScalaSymbolTag)
351+
ref(defn.ScalaSymbolModule)
352+
.select(defn.ScalaSymbolModule_apply)
353+
.appliedTo(Literal(Constant(tree.const.scalaSymbolValue.name)))
354+
else
355+
super.typedLiteral(tree)
349356

350357
/** Type check select nodes, applying the following rewritings exhaustively
351358
* on selections `e.m`, where `OT` is the type of the owner of `m` and `ET`

compiler/src/dotty/tools/dotc/typer/ReTyper.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class ReTyper extends Typer {
3939
untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
4040
}
4141

42-
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
42+
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Tree =
4343
promote(tree)
4444

4545
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
449449
}
450450
}
451451

452-
def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") {
452+
def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Tree = track("typedLiteral") {
453453
assignType(tree)
454454
}
455455

docs/docs/internals/syntax.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ SimpleLiteral ::= [‘-’] integerLiteral
9999
| booleanLiteral
100100
| characterLiteral
101101
| stringLiteral
102+
| symbolLiteral
102103
Literal ::= SimpleLiteral
103104
| processedStringLiteral
104-
| symbolLiteral
105105
| ‘null’
106106
107107
QualId ::= id {‘.’ id}

tests/neg/singletons.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@ object Test {
55

66
val n: null = null // error: Null is not a legal singleton type // error: only classes can have declared but undefined members
77

8-
val sym: 'sym = 'sym // error: Symbol is not a legal singleton type // error: only classes can have declared but undefined members
9-
108
val foo: s"abc" = "abc" // error: not a legal singleton type // error: only classes can have declared but undefined members
119
}

tests/neg/sip23-symbols.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object Test {
2+
val sym0 = 's
3+
//sym0: Symbol
4+
sym0: 's // error
5+
6+
//val sym1: 's = 's
7+
//sym1: Symbol
8+
//sym1: 's
9+
10+
//final val sym2 = 's
11+
//sym2: Symbol
12+
//sym2: 's
13+
14+
def id[T](t: T): T = t
15+
type Identity[T] = T
16+
def narrow[T <: Singleton](t: T): Identity[T] = t
17+
18+
final val sym3 = id('s)
19+
//sym3: Symbol
20+
sym3: 's // error
21+
22+
//val sym4 = narrow('s)
23+
//sym4: Symbol
24+
//sym4: 's
25+
}

tests/pickling/literals.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test {
2+
val a: 1 = 1
3+
val b: 1L = 1L
4+
val c: 1F = 1F
5+
val d: 1.0 = 1.0
6+
val e: true = true
7+
val f: '*' = '*'
8+
val g: 'a = 'a
9+
}

tests/pos/singletontrait.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
def foo[T <: Singleton](x: T): T = x
3+
4+
val a: 1 = foo(1)
5+
}

tests/pos/sip23-symbols.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object Test {
2+
val sym0 = 's
3+
sym0: Symbol
4+
//sym0: 's
5+
6+
val sym1: 's = 's
7+
sym1: Symbol
8+
sym1: 's
9+
10+
final val sym2 = 's
11+
sym2: Symbol
12+
sym2: 's
13+
14+
def id[T](t: T): T = t
15+
type Identity[T] = T
16+
def narrow[T <: Singleton](t: T): Identity[T] = t
17+
18+
final val sym3 = id('s)
19+
sym3: Symbol
20+
//sym3: 's
21+
22+
val sym4 = narrow('s)
23+
sym4: Symbol
24+
sym4: 's
25+
}

0 commit comments

Comments
 (0)