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

Commit 52e949e

Browse files
author
Jon Pretty
committed
Merge pull request #86 from folone/sip23-typelevel
SIP-23
2 parents d371746 + e04c588 commit 52e949e

39 files changed

+300
-82
lines changed

bincompat-backward.whitelist.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ filter {
207207
{
208208
matchName="scala.collection.immutable.Stream.scala$collection$immutable$Stream$$loop$4"
209209
problemName=MissingMethodProblem
210+
},
211+
{
212+
matchName="scala.reflect.api.Types.LiteralType"
213+
problemName=MissingMethodProblem
210214
}
211215
]
212216
}

bincompat-forward.whitelist.conf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,18 @@ filter {
393393
{
394394
matchName="scala.reflect.runtime.Settings.ZirrefutableGeneratorPatterns"
395395
problemName=MissingMethodProblem
396+
},
397+
{
398+
matchName="scala.reflect.api.Types$LiteralTypeApi"
399+
problemName=MissingClassProblem
400+
},
401+
{
402+
matchName="scala.reflect.api.Types.LiteralType"
403+
problemName=MissingMethodProblem
404+
},
405+
{
406+
matchName="scala.reflect.api.Types$LiteralTypeExtractor"
407+
problemName=MissingClassProblem
396408
}
397409
]
398410
}

spec/03-types.md

Lines changed: 29 additions & 4 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,40 @@ forms.
102103
### Singleton Types
103104

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

108109
A singleton type is of the form $p.$`type`, where $p$ is a
109110
path pointing to a value expected to [conform](06-expressions.html#expression-typing)
110111
to `scala.AnyRef`. The type denotes the set of values
111-
consisting of `null` and the value denoted by $p$.
112+
consisting of `null` and the value denoted by $p$
113+
(i.e., the value $v$ for which `v eq p`).
112114

113-
A _stable type_ is either a singleton type or a type which is
114-
declared to be a subtype of trait `scala.Singleton`.
115+
<!-- a pattern match/type test against a singleton type `p.type` desugars to `_ eq p` -->
116+
117+
### Literal Types
118+
119+
```ebnf
120+
SimpleType ::= Literal
121+
```
122+
123+
A literal type `lit` is a special kind of singleton type which denotes the single literal value `lit`.
124+
Thus, the type ascription `1: 1` gives the most precise type to the literal value `1`: the literal type `1`.
125+
126+
At run time, an expression `e` is considered to have literal type `lit` if `e == lit`.
127+
Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ : lit => }` is determined by evaluating `e == lit`.
128+
129+
<!-- TODO: use eq when we lift it up to Any -->
130+
131+
<!-- TODO: relate to constant types, which trigger constant folding
132+
ConstantType(1).deconst =:= LiteralType(1)
133+
LiteralType(1).widen =:= IntClass.tpe
134+
-->
135+
136+
137+
### Stable Types
138+
A _stable type_ is a singleton type, a literal type,
139+
or a type that is declared to be a subtype of trait `scala.Singleton`.
115140

116141
### Type Projection
117142

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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,11 @@ A type pattern $T$ is of one of the following forms:
337337
be used as type patterns, because they would match nothing in any case.
338338

339339
* A singleton type `$p$.type`. This type pattern matches only the value
340-
denoted by the path $p$ (that is, a pattern match involved a
341-
comparison of the matched value with $p$ using method `eq` in class
342-
`AnyRef`).
340+
denoted by the path $p$ (the `eq` method is used to compare the matched value to $p$).
341+
342+
* A literal type `$lit$`. This type pattern matches only the value
343+
denoted by the literal $lit$ (the `==` method is used to compare the matched value to $lit$). <!-- SIP-23 -->
344+
343345
* A compound type pattern `$T_1$ with $\ldots$ with $T_n$` where each $T_i$ is a
344346
type pattern. This type pattern matches all values that are matched by each of
345347
the type patterns $T_i$.

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
7878

7979
override def settings = currentSettings
8080

81+
// TODO: temporary flag to easily enable/disable SIP-23 (aka the type formerly known as 42.type)
82+
override def sip23: Boolean = settings.Xexperimental.value
83+
8184
/** Switch to turn on detailed type logs */
8285
var printTypings = settings.Ytyperdebug.value
8386

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

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ self =>
339339
}
340340
private def inScalaRootPackage = inScalaPackage && currentPackage == "scala"
341341

342+
342343
def parseStartRule: () => Tree
343344

344345
def parseRule[T](rule: this.type => T): T = {
@@ -675,11 +676,11 @@ self =>
675676

676677
def isExprIntro: Boolean = isExprIntroToken(in.token)
677678

678-
def isTypeIntroToken(token: Token): Boolean = token match {
679+
def isTypeIntroToken(token: Token): Boolean = (sip23 && isLiteralToken(token)) || (token match {
679680
case IDENTIFIER | BACKQUOTED_IDENT | THIS |
680681
SUPER | USCORE | LPAREN | AT => true
681682
case _ => false
682-
}
683+
})
683684

684685
def isStatSeqEnd = in.token == RBRACE || in.token == EOF
685686

@@ -932,31 +933,17 @@ self =>
932933
* | SimpleType `#' Id
933934
* | StableId
934935
* | Path `.' type
936+
* | Literal
935937
* | `(' Types `)'
936938
* | WildcardType
937939
* }}}
938940
*/
939941
def simpleType(): Tree = {
940942
val start = in.offset
941943
simpleTypeRest(in.token match {
942-
case LPAREN =>
943-
atPos(start)(makeTupleType(inParens(types())))
944-
case LBRACKET =>
945-
atPos(start) {
946-
val ts = typeParamClauseOpt(freshTypeName("typelambda"), null)
947-
if (ts.isEmpty) {
948-
syntaxError("missing type parameters", skipIt = false)
949-
errorTypeTree
950-
} else if (in.token == ARROW) {
951-
in.skipToken()
952-
makeTypeLambdaTypeTree(ts, typ())
953-
} else {
954-
syntaxError("`=>' expected", skipIt = false)
955-
errorTypeTree
956-
}
957-
}
958-
case USCORE =>
959-
wildcardType(in.skipToken())
944+
case LPAREN => atPos(start)(makeTupleType(inParens(types())))
945+
case USCORE => wildcardType(in.skipToken())
946+
case tok if sip23 && isLiteralToken(tok) => atPos(start){SingletonTypeTree(literal())} // SIP-23
960947
case _ =>
961948
path(thisOK = false, typeOK = true) match {
962949
case r @ SingletonTypeTree(_) => r
@@ -1038,7 +1025,15 @@ self =>
10381025
else
10391026
mkOp(infixType(InfixMode.RightOp))
10401027
}
1028+
// SIP-23
1029+
def isNegatedLiteralType = sip23 && (
1030+
t match { // the token for `t` (Ident("-")) has already been read, thus `isLiteral` below is looking at next token (must be a literal)
1031+
case Ident(name) if isLiteral => name == nme.MINUS.toTypeName // TODO: OPT? lift out nme.MINUS.toTypeName?
1032+
case _ => false
1033+
}
1034+
)
10411035
if (isIdent) checkRepeatedParam orElse asInfix
1036+
else if (isNegatedLiteralType) atPos(t.pos.start){SingletonTypeTree(literal(isNegated = true, start = t.pos.start))}
10421037
else t
10431038
}
10441039

src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ trait TypeKinds { self: ICodes =>
383383
case ThisType(ArrayClass) => ObjectReference
384384
case ThisType(sym) => REFERENCE(sym)
385385
case SingleType(_, sym) => primitiveOrRefType(sym)
386+
case LiteralType(_) => toTypeKind(t.underlying)
386387
case ConstantType(_) => toTypeKind(t.underlying)
387388
case TypeRef(_, sym, args) => primitiveOrClassType(sym, args)
388389
case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!")

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
435435
case ThisType(ArrayClass) => ObjectReference // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
436436
case ThisType(sym) => getClassBTypeAndRegisterInnerClass(sym)
437437
case SingleType(_, sym) => primitiveOrClassToBType(sym)
438+
case LiteralType(_) => toTypeKind(t.underlying)
438439
case ConstantType(_) => toTypeKind(t.underlying)
439440
case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
440441
}

src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ abstract class Pickler extends SubComponent {
223223
case SingleType(pre, sym) =>
224224
putType(pre)
225225
putSymbol(sym)
226+
case LiteralType(value) =>
227+
putConstant(value)
226228
case SuperType(thistpe, supertpe) =>
227229
putType(thistpe)
228230
putType(supertpe)
@@ -452,6 +454,7 @@ abstract class Pickler extends SubComponent {
452454
case NoType | NoPrefix =>
453455
case ThisType(sym) => writeRef(sym)
454456
case SingleType(pre, sym) => writeRef(pre) ; writeRef(sym)
457+
case LiteralType(value) => writeRef(value)
455458
case SuperType(thistpe, supertpe) => writeRef(thistpe) ; writeRef(supertpe)
456459
case ConstantType(value) => writeRef(value)
457460
case TypeBounds(lo, hi) => writeRef(lo) ; writeRef(hi)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ abstract class Erasure extends AddInterfaces
878878
List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
879879
List()) setPos tree.pos
880880
targ.tpe match {
881-
case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
881+
case SingleType(_, _) | LiteralType(_) | ThisType(_) | SuperType(_, _) =>
882882
val cmpOp = if (targ.tpe <:< AnyValTpe) Any_equals else Object_eq
883883
atPos(tree.pos) {
884884
Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ abstract class UnCurry extends InfoTransform
322322
args.take(formals.length - 1) :+ (suffix setType formals.last)
323323
}
324324

325-
val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args
325+
val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head.widen) else args
326326

327327
map2(formals, args1) { (formal, arg) =>
328328
if (!isByNameParamType(formal))

src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,16 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
447447
// - Scala's arrays are invariant (so we don't drop type tests unsoundly)
448448
if (extractorArgTypeTest) mkDefault
449449
else expectedTp match {
450-
case SingleType(_, sym) => mkEqTest(gen.mkAttributedQualifier(expectedTp)) // SI-4577, SI-4897
450+
case SingleType(_, sym) =>
451+
val expected = gen.mkAttributedQualifier(expectedTp)
452+
if (expectedTp <:< AnyRefTpe) mkEqTest(expected) // SI-4577, SI-4897
453+
else mkEqualsTest(expected)
454+
// TODO SIP-23: should we test equality for literal types with eq?
455+
// Conceptually cleaner, as SingleType is tested using eq.
456+
// In practice it doesn't really matter, since `equals` does the same thing as `eq` in the `AnyVal` subclasses of `Any`.
457+
// Should revisit if we end up lifting `eq`'s definition to `Any`, as discussed here:
458+
// https://groups.google.com/d/msg/scala-internals/jsVlJI4H5OQ/8emZWRmgzcoJ
459+
case LiteralType(const) => mkEqualsTest(expTp(Literal(const)))
451460
case ThisType(sym) if sym.isModule => and(mkEqualsTest(CODE.REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil
452461
case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(CODE.NULL))
453462
case ConstantType(const) => mkEqualsTest(expTp(Literal(const)))

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,7 @@ trait Implicits {
11601160
// necessary only to compile typetags used inside the Universe cake
11611161
case ThisType(thisSym) =>
11621162
gen.mkAttributedThis(thisSym)
1163+
// TODO SIP-23: TypeTag for LiteralType
11631164
case _ =>
11641165
// if `pre` is not a PDT, e.g. if someone wrote
11651166
// implicitly[scala.reflect.macros.blackbox.Context#TypeTag[Int]]

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,28 @@ trait Namers extends MethodSynthesis {
825825
}
826826
}
827827

828-
/** This method has a big impact on the eventual compiled code.
828+
private def refersToSymbolLessAccessibleThan(tp: Type, sym: Symbol): Boolean = {
829+
val accessibilityReference =
830+
if (sym.isValue && sym.owner.isClass && sym.isPrivate)
831+
sym.getterIn(sym.owner)
832+
else sym
833+
834+
@tailrec def loop(tp: Type): Boolean = tp match {
835+
case SingleType(pre, sym) =>
836+
(sym isLessAccessibleThan accessibilityReference) || loop(pre)
837+
case ThisType(sym) =>
838+
sym isLessAccessibleThan accessibilityReference
839+
case p: SimpleTypeProxy =>
840+
loop(p.underlying)
841+
case _ =>
842+
false
843+
}
844+
845+
loop(tp)
846+
}
847+
848+
/**
849+
* This method has a big impact on the eventual compiled code.
829850
* At this point many values have the most specific possible
830851
* type (e.g. in val x = 42, x's type is Int(42), not Int) but
831852
* most need to be widened to avoid undesirable propagation of
@@ -838,35 +859,39 @@ trait Namers extends MethodSynthesis {
838859
* value should not be widened, so it has a use even in situations
839860
* whether it is otherwise redundant (such as in a singleton.)
840861
*/
841-
private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type = {
842-
val getter =
843-
if (sym.isValue && sym.owner.isClass && sym.isPrivate)
844-
sym.getter(sym.owner)
845-
else sym
846-
def isHidden(tp: Type): Boolean = tp match {
847-
case SingleType(pre, sym) =>
848-
(sym isLessAccessibleThan getter) || isHidden(pre)
849-
case ThisType(sym) =>
850-
sym isLessAccessibleThan getter
851-
case p: SimpleTypeProxy =>
852-
isHidden(p.underlying)
853-
case _ =>
854-
false
855-
}
856-
val shouldWiden = (
857-
!tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
858-
&& (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
859-
&& ( sym.isVariable
860-
|| sym.isMethod && !sym.hasAccessorFlag
861-
|| isHidden(tpe)
862-
)
863-
)
864-
dropIllegalStarTypes(
862+
private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type =
863+
if (sip23) { // SIP-23
864+
// TODO: spec -- this is a crucial part of type inference
865+
// NOTES:
866+
// - Can we widen less? (E.g., for local definitions.)
867+
// - Do we need to check tpe.deconst <:< pt?
868+
// - We don't need to call dropIllegalStarTypes on a ref to a module class, do we? Where would the stars be? In the prefix?
869+
870+
// We're inferring the result type of a stable symbol, and the type doesn't refer to a hidden symbol
871+
val mayKeepSingletonType = sym.isStable && !refersToSymbolLessAccessibleThan(tpe, sym)
872+
873+
// (OPT: 99.99% of the time, pt will be WildcardType)
874+
@inline def cannotWiden = (pt ne WildcardType) && !(tpe.widen <:< pt)
875+
876+
// If the definition can keep its inferred singleton type,
877+
// or widening would mean no longer conforming to the expected type,
878+
// we must still deconst unless it's a final val. Otherwise, widen.
879+
if (mayKeepSingletonType || cannotWiden) { if (sym.isFinal) tpe else tpe.deconst }
880+
else tpe.widen
881+
} else {
882+
val shouldWiden = (
883+
!tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
884+
&& (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
885+
&& ( sym.isVariable
886+
|| sym.isMethod && !sym.hasAccessorFlag
887+
|| refersToSymbolLessAccessibleThan(tpe, sym)
888+
)
889+
)
865890
if (shouldWiden) tpe.widen
866891
else if (sym.isFinal) tpe // "final val" allowed to retain constant type
867892
else tpe.deconst
868-
)
869-
}
893+
}
894+
870895
/** Computes the type of the body in a ValDef or DefDef, and
871896
* assigns the type to the tpt's node. Returns the type.
872897
*/
@@ -876,7 +901,7 @@ trait Namers extends MethodSynthesis {
876901
case _ => defnTyper.computeType(tree.rhs, pt)
877902
}
878903

879-
val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt)
904+
val defnTpe = dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt))
880905
tree.tpt defineType defnTpe setPos tree.pos.focus
881906
tree.tpt.tpe
882907
}

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5093,13 +5093,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
50935093
}
50945094

50955095
def typedSingletonTypeTree(tree: SingletonTypeTree) = {
5096+
// SIP-23: don't require AnyRef for 1.type etc
5097+
val pt = if (sip23) WildcardType else AnyRefTpe
50965098
val refTyped =
50975099
context.withImplicitsDisabled {
5098-
typed(tree.ref, MonoQualifierModes | mode.onlyTypePat, AnyRefTpe)
5100+
typed(tree.ref, MonoQualifierModes | mode.onlyTypePat, pt)
50995101
}
51005102

5103+
// .resultType unwraps NullaryMethodType (accessor of a path)
5104+
// .deconst unwraps the ConstantType to a LiteralType (for literal-based singleton types)
51015105
if (!refTyped.isErrorTyped)
5102-
tree setType refTyped.tpe.resultType
5106+
tree setType refTyped.tpe.resultType.deconst
51035107

51045108
if (treeInfo.admitsTypeSelection(refTyped)) tree
51055109
else UnstableTreeError(refTyped)

0 commit comments

Comments
 (0)