Skip to content
This repository was archived by the owner on Sep 1, 2020. It is now read-only.

Commit d777d40

Browse files
milessabinfoloneadriaanm
committed
SIP-23 Implementation of literal types
Co-Authored-By: George Leontiev <[email protected]> Co-Authored-By: Adriaan Moors <[email protected]>
1 parent 7ff86fc commit d777d40

File tree

141 files changed

+1650
-160
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+1650
-160
lines changed

bincompat-forward.whitelist.conf

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,22 @@ filter {
114114
{
115115
matchName="scala.reflect.runtime.Settings.YkindPolymorphism"
116116
problemName=DirectMissingMethodProblem
117+
},
118+
{
119+
matchName="scala.reflect.runtime.Settings.YliteralTypes"
120+
problemName=MissingMethodProblem
121+
},
122+
{
123+
matchName="scala.ValueOf"
124+
problemName=MissingClassProblem
125+
},
126+
{
127+
matchName="scala.Predef.valueOf"
128+
problemName=DirectMissingMethodProblem
129+
},
130+
{
131+
matchName="scala.ValueOf$"
132+
problemName=MissingClassProblem
117133
}
118134
]
119135
}

project/ScalaOptionParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object ScalaOptionParser {
8686
"-Xno-forwarders", "-Xno-patmat-analysis", "-Xno-uescape", "-Xnojline", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xstrict-inference", "-Xverify", "-Y",
8787
"-Ybreak-cycles", "-Ydebug", "-Ycompact-trees", "-YdisableFlatCpCaching", "-Ydoc-debug",
8888
"-Yide-debug", "-Yinfer-argument-types",
89-
"-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
89+
"-Yissue-debug", "-Yliteral-types", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
9090
"-Yno-completion", "-Yno-generic-signatures", "-Yno-imports", "-Yno-predef",
9191
"-Yoverride-objects", "-Yoverride-vars", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypartial-unification", "-Ypos-debug", "-Ypresentation-debug",
9292
"-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based",

spec/01-lexical-syntax.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,9 @@ string literal does not start a valid escape sequence.
523523
symbolLiteral ::= ‘'’ plainid
524524
```
525525
526-
A symbol literal `'x` is a shorthand for the expression
527-
`scala.Symbol("x")`. `Symbol` is a [case class](05-classes-and-objects.html#case-classes),
528-
which is defined as follows.
526+
A symbol literal `'x` is a shorthand for the expression `scala.Symbol("x")` and
527+
is of the [literal type](03-types#literal-types) `'x`. `Symbol` is a [case
528+
class](05-classes-and-objects.html#case-classes), which is defined as follows.
529529
530530
```scala
531531
package scala

spec/03-types.md

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ chapter: 3
2323
| SimpleType ‘#’ id
2424
| StableId
2525
| Path ‘.’ ‘type’
26+
| Literal
2627
| ‘(’ Types ‘)’
2728
TypeArgs ::= ‘[’ Types ‘]’
2829
Types ::= Type {‘,’ Type}
@@ -102,16 +103,46 @@ forms.
102103
### Singleton Types
103104

104105
```ebnf
105-
SimpleType ::= Path ‘.’ type
106+
SimpleType ::= Path ‘.’ type
106107
```
107108

108-
A _singleton type_ is of the form $p.$`type`, where $p$ is a
109-
path pointing to a value expected to [conform](06-expressions.html#expression-typing)
110-
to `scala.AnyRef`. The type denotes the set of values
111-
consisting of `null` and the value denoted by $p$.
109+
A _singleton type_ is of the form $p.$`type`. Where $p$ is a path pointing to a
110+
value which [conforms](06-expressions.html#expression-typing) to
111+
`scala.AnyRef`, the type denotes the set of values consisting of `null` and the
112+
value denoted by $p$ (i.e., the value $v$ for which `v eq p`). Where the path
113+
does not conform to `scala.AnyRef` the type denotes the set consisting of only
114+
the value denoted by $p$.
112115

113-
A _stable type_ is either a singleton type or a type which is
114-
declared to be a subtype of trait `scala.Singleton`.
116+
<!-- a pattern match/type test against a singleton type `p.type` desugars to `_ eq p` -->
117+
118+
### Literal Types
119+
120+
```ebnf
121+
SimpleType ::= Literal
122+
```
123+
124+
A literal type `lit` is a special kind of singleton type which denotes the
125+
single literal value `lit`. Thus, the type ascription `1: 1` gives the most
126+
precise type to the literal value `1`: the literal type `1`.
127+
128+
At run time, an expression `e` is considered to have literal type `lit` if `e ==
129+
lit`. Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ :
130+
lit => }` is determined by evaluating `e == lit`.
131+
132+
Literal types are available for all types for which there is dedicated syntax
133+
except `Unit`. This includes the numeric types (other than `Byte` and `Short`
134+
which don't currently have syntax), `Boolean`, `Char`, `String` and `Symbol`.
135+
136+
<!-- TODO: use eq when we lift it up to Any -->
137+
138+
<!-- TODO: add Byte when Byte literals are added -->
139+
140+
<!-- TODO: there is a relationship with constant folding but the latter
141+
isn't currently specified at all -->
142+
143+
### Stable Types
144+
A _stable type_ is a singleton type, a literal type,
145+
or a type that is declared to be a subtype of trait `scala.Singleton`.
115146

116147
### Type Projection
117148

spec/06-expressions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ $T$ forSome { type $t_1[\mathit{tps}\_1] >: L_1 <: U_1$; $\ldots$; type $t_n[\ma
7676
SimpleExpr ::= Literal
7777
```
7878

79-
Typing of literals is as described [here](01-lexical-syntax.html#literals); their
80-
evaluation is immediate.
79+
Typing of literals is described along with their [lexical syntax](01-lexical-syntax.html#literals);
80+
their evaluation is immediate.
8181

8282
## The _Null_ Value
8383

spec/08-pattern-matching.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,13 @@ A type pattern $T$ is of one of the following forms:
339339
be used as type patterns, because they would match nothing in any case.
340340

341341
* A singleton type `$p$.type`. This type pattern matches only the value
342-
denoted by the path $p$ (that is, a pattern match involved a
343-
comparison of the matched value with $p$ using method `eq` in class
344-
`AnyRef`).
342+
denoted by the path $p$ (the `eq` method is used to compare the matched value
343+
to $p$).
344+
345+
* A literal type `$lit$`. This type pattern matches only the value
346+
denoted by the literal $lit$ (the `==` method is used to compare the matched
347+
value to $lit$).
348+
345349
* A compound type pattern `$T_1$ with $\ldots$ with $T_n$` where each $T_i$ is a
346350
type pattern. This type pattern matches all values that are matched by each of
347351
the type patterns $T_i$.

spec/12-the-scala-standard-library.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,12 @@ object Predef {
642642
def classOf[T]: Class[T] = null
643643
// this is a dummy, classOf is handled by compiler.
644644

645+
// valueOf -----------------------------------------------------------
646+
647+
/** Retrieve the single value of a type with a unique inhabitant. */
648+
@inline def valueOf[T](implicit vt: ValueOf[T]): T {} = vt.value
649+
// instances of the ValueOf type class are provided by the compiler.
650+
645651
// Standard type aliases ---------------------------------------------
646652

647653
type String = java.lang.String

src/compiler/scala/reflect/macros/contexts/Evals.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ trait Evals {
88
self: Context =>
99

1010
private lazy val evalMirror = ru.runtimeMirror(universe.analyzer.defaultMacroClassloader)
11-
private lazy val evalToolBox = evalMirror.mkToolBox()
11+
private lazy val evalToolBox = evalMirror.mkToolBox(options = if(global.settings.YliteralTypes) "-Yliteral-types" else "")
1212
private lazy val evalImporter = ru.internal.createImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }]
1313

1414
def eval[T](expr: Expr[T]): T = {
@@ -20,4 +20,4 @@ trait Evals {
2020
evalToolBox.eval(imported).asInstanceOf[T]
2121
}
2222
}
23-
}
23+
}

src/compiler/scala/reflect/reify/phases/Reify.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ trait Reify extends GenSymbols
5151
case mods: global.Modifiers => reifyModifiers(mods)
5252
case xs: List[_] => reifyList(xs)
5353
case s: String => Literal(Constant(s))
54+
case s: scala.Symbol if settings.YliteralTypes
55+
=> Literal(Constant(s))
5456
case v if isAnyVal(v) => Literal(Constant(v))
5557
case null => Literal(Constant(null))
5658
case _ =>

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -706,11 +706,11 @@ self =>
706706

707707
def isExprIntro: Boolean = isExprIntroToken(in.token)
708708

709-
def isTypeIntroToken(token: Token): Boolean = token match {
709+
def isTypeIntroToken(token: Token): Boolean = (settings.YliteralTypes && isLiteralToken(token)) || (token match {
710710
case IDENTIFIER | BACKQUOTED_IDENT | THIS |
711711
SUPER | USCORE | LPAREN | AT => true
712712
case _ => false
713-
}
713+
})
714714

715715
def isStatSeqEnd = in.token == RBRACE || in.token == EOF
716716

@@ -1013,6 +1013,7 @@ self =>
10131013
* | SimpleType `#' Id
10141014
* | StableId
10151015
* | Path `.' type
1016+
* | Literal
10161017
* | `(' Types `)'
10171018
* | WildcardType
10181019
* }}}
@@ -1022,6 +1023,7 @@ self =>
10221023
simpleTypeRest(in.token match {
10231024
case LPAREN => atPos(start)(makeSafeTupleType(inParens(types()), start))
10241025
case USCORE => wildcardType(in.skipToken())
1026+
case tok if settings.YliteralTypes && isLiteralToken(tok) => atPos(start){SingletonTypeTree(literal())}
10251027
case _ =>
10261028
path(thisOK = false, typeOK = true) match {
10271029
case r @ SingletonTypeTree(_) => r
@@ -1103,7 +1105,14 @@ self =>
11031105
else
11041106
mkOp(infixType(InfixMode.RightOp))
11051107
}
1108+
def isNegatedLiteralType = settings.YliteralTypes && (
1109+
t match { // the token for `t` (Ident("-")) has already been read, thus `isLiteral` below is looking at next token (must be a literal)
1110+
case Ident(name) if isLiteral => name == nme.MINUS.toTypeName
1111+
case _ => false
1112+
}
1113+
)
11061114
if (isIdent) checkRepeatedParam orElse asInfix
1115+
else if (isNegatedLiteralType) atPos(t.pos.start){SingletonTypeTree(literal(isNegated = true, start = t.pos.start))}
11071116
else t
11081117
}
11091118

@@ -1259,9 +1268,12 @@ self =>
12591268
*/
12601269
def literal(isNegated: Boolean = false, inPattern: Boolean = false, start: Offset = in.offset): Tree = atPos(start) {
12611270
def finish(value: Any): Tree = try newLiteral(value) finally in.nextToken()
1262-
if (in.token == SYMBOLLIT)
1263-
Apply(scalaDot(nme.Symbol), List(finish(in.strVal)))
1264-
else if (in.token == INTERPOLATIONID)
1271+
if (in.token == SYMBOLLIT) {
1272+
if(settings.YliteralTypes)
1273+
finish(Symbol(in.strVal.intern()))
1274+
else
1275+
Apply(scalaDot(nme.Symbol), List(finish(in.strVal)))
1276+
} else if (in.token == INTERPOLATIONID)
12651277
interpolatedString(inPattern = inPattern)
12661278
else finish(in.token match {
12671279
case CHARLIT => in.charVal
@@ -2603,6 +2615,11 @@ self =>
26032615
if (!tp.isEmpty && newmods.isMutable &&
26042616
(lhs.toList forall (_.isInstanceOf[Ident])) && in.token == USCORE) {
26052617
in.nextToken()
2618+
tp match {
2619+
case SingletonTypeTree(Literal(Constant(_))) =>
2620+
syntaxError(tp.pos, "default initialization prohibited for literal-typed vars", skipIt = false)
2621+
case _ =>
2622+
}
26062623
newmods = newmods | Flags.DEFAULTINIT
26072624
EmptyTree
26082625
} else {

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ trait ScalaSettings extends AbsScalaSettings
3030
protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".")
3131

3232
/** Enabled under -Xexperimental. */
33-
protected def experimentalSettings = List[BooleanSetting](YpartialUnification, YinductionHeuristics, YkindPolymorphism)
33+
protected def experimentalSettings = List[BooleanSetting](YpartialUnification, YinductionHeuristics, YkindPolymorphism, YliteralTypes)
3434

3535
/** Enabled under -Xfuture. */
3636
protected def futureSettings = List[BooleanSetting]()
@@ -223,6 +223,7 @@ trait ScalaSettings extends AbsScalaSettings
223223
val Yvirtpatmat = BooleanSetting ("-Yvirtpatmat", "Enable pattern matcher virtualization")
224224
val YinductionHeuristics = BooleanSetting ("-Yinduction-heuristics", "Enable induction heuristics in implicit resolution")
225225
val YkindPolymorphism = BooleanSetting ("-Ykind-polymorphism", "Enable kind polymorphism")
226+
val YliteralTypes = BooleanSetting ("-Yliteral-types", "Enable literal-based singleton types")
226227

227228
val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
228229
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")

src/compiler/scala/tools/nsc/transform/Constructors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
598598

599599
private def triage() = {
600600
// Constant typed vals are not memoized.
601-
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType]
601+
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[FoldableConstantType]
602602

603603
// The early initialized field definitions of the class (these are the class members)
604604
val presupers = treeInfo.preSuperFields(stats)

0 commit comments

Comments
 (0)