Permalink
Browse files

Literal-based singleton types. This means things like:

  var x: 3.type = 3
  x = 4 // error

All tests pass here after indiscriminately updating various checkfiles.
  • Loading branch information...
1 parent 85cd96d commit 824ce5efdcc1b77b7fbb79d9239d33396281eb64 @paulp committed Jun 2, 2012
@@ -316,6 +316,12 @@ trait Types extends api.Types { self: SymbolTable =>
/** The base class for all types */
abstract class Type extends AbsTypeImpl with Annotatable[Type] {
+ var isDeclaredSingleton: Boolean = false
+ def asDeclaredSingleton: this.type = {
+ isDeclaredSingleton = true
+ this
+ }
+
/** Types for which asSeenFrom always is the identity, no matter what
* prefix or owner.
*/
@@ -1873,13 +1879,18 @@ trait Types extends api.Types { self: SymbolTable =>
assert(underlying.typeSymbol != UnitClass)
override def isTrivial: Boolean = true
override def isNotNull = value.value != null
- override def deconst: Type = underlying
- override def safeToString: String =
- underlying.toString + "(" + value.escapedStringValue + ")"
+ override def deconst: Type =
+ if (isDeclaredSingleton) this
+ else underlying
+ override def safeToString: String = value.escapedStringValue + ".type"
// override def isNullable: Boolean = value.value eq null
// override def isNonNull: Boolean = value.value ne null
override def kind = "ConstantType"
}
+ abstract class DeclaredSingletonConstantType(value: Constant) extends ConstantType(value) {
+ override def deconst: Type = this
+ override def kind = "DeclaredSingletonConstantType"
+ }
final class UniqueConstantType(value: Constant) extends ConstantType(value) with UniqueType {
/** Save the type of `value`. For Java enums, it depends on finding the linked class,
@@ -636,13 +636,11 @@ self =>
def isExprIntro: Boolean = isExprIntroToken(in.token)
- def isTypeIntroToken(token: Int): Boolean = token match {
+ def isTypeIntroToken(token: Int): Boolean = isLiteralToken(token) || (token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS |
SUPER | USCORE | LPAREN | AT => true
case _ => false
- }
-
- def isTypeIntro: Boolean = isTypeIntroToken(in.token)
+ })
def isStatSeqEnd = in.token == RBRACE || in.token == EOF
@@ -1025,6 +1023,12 @@ self =>
if (in.token == DOT) t = selectors(t, typeOK, in.skipToken())
} else {
val tok = in.token
+ if (tok != BACKQUOTED_IDENT && tok != IDENTIFIER && tok != NULL) {
+ val lit = literal(false)
+ accept(DOT)
+ accept(TYPE)
+ return atPos(start)(new SingletonTypeTree(lit) { override val isLiteral = true })
+ }
val name = ident()
t = atPos(start) {
if (tok == BACKQUOTED_IDENT) Ident(name) withAttachment BackquotedIdentifier
@@ -20,7 +20,7 @@ trait MatrixAdditions extends ast.TreeDSL {
import CODE._
import Debug._
import treeInfo._
- import definitions.{ isPrimitiveValueClass }
+ import definitions.isPrimitiveValueClass
/** The Squeezer, responsible for all the squeezing.
*/
@@ -141,7 +141,8 @@ trait MatrixAdditions extends ast.TreeDSL {
(sym.isMutable) && // indicates that have not yet checked exhaustivity
!(sym hasFlag NO_EXHAUSTIVE) && // indicates @unchecked
(sym.tpe.typeSymbol.isSealed) &&
- !isPrimitiveValueClass(sym.tpe.typeSymbol) // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte
+ // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte
+ !isPrimitiveValueClass(sym.tpe.typeSymbol)
}
private lazy val inexhaustives: List[List[Combo]] = {
@@ -511,6 +511,7 @@ trait Infer {
else Some(
if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass)
else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass)
+ else if (targ.isDeclaredSingleton) targ
// this infers Foo.type instead of "object Foo" (see also widenIfNecessary)
else if (targ.typeSymbol.isModuleClass || ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden)) targ
else targ.widen
@@ -1262,6 +1263,8 @@ trait Infer {
tp match {
case SingleType(pre, _) =>
check(pre, bound)
+ case ConstantType(_) =>
+ None
case TypeRef(pre, sym, args) =>
if (sym.isAbstractType) {
if (!isLocalBinding(sym)) patternWarning(tp, "abstract type ")
@@ -26,6 +26,8 @@ trait Namers extends MethodSynthesis {
private var _lockedCount = 0
def lockedCount = this._lockedCount
+ private var inferDependentTypes = false
+
/** Replaces any Idents for which cond is true with fresh TypeTrees().
* Does the same for any trees containing EmptyTrees.
*/
@@ -750,7 +752,7 @@ trait Namers extends MethodSynthesis {
* value should not be widened, so it has a use even in situations
* whether it is otherwise redundant (such as in a singleton.)
*/
- private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type = {
+ private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type, isLiteral: Boolean): Type = {
val getter =
if (sym.isValue && sym.owner.isClass && sym.isPrivate)
sym.getter(sym.owner)
@@ -774,6 +776,7 @@ trait Namers extends MethodSynthesis {
if (tpe.typeSymbolDirect.isModuleClass) tpe1
else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag)
if (tpe2 <:< pt) tpe2 else tpe1
+ else if (isLiteral && inferDependentTypes) tpe
else if (isHidden(tpe)) tpe2
// In an attempt to make pattern matches involving method local vals
// compilable into switches, for a time I had a more generous condition:
@@ -792,7 +795,7 @@ trait Namers extends MethodSynthesis {
if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt)
else defnTyper.computeType(tree.rhs, pt)
- val typedDefn = widenIfNecessary(tree.symbol, typedBody, pt)
+ val typedDefn = widenIfNecessary(tree.symbol, typedBody, pt, tree.rhs.isInstanceOf[Literal])
assignTypeToTree(tree, typedDefn)
}
@@ -1034,6 +1037,7 @@ trait Namers extends MethodSynthesis {
rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List()))
else rt
})
+
}
/**
@@ -4930,10 +4930,13 @@ trait Typers extends Modes with Adaptations with Taggings {
if (value.tag == UnitTag) UnitClass.tpe
else ConstantType(value))
- case SingletonTypeTree(ref) =>
- val ref1 = checkStable(
- typed(ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe))
- tree setType ref1.tpe.resultType
+ case stt @ SingletonTypeTree(ref) =>
+ val pt = if (stt.isLiteral) AnyClass.tpe else AnyRefClass.tpe
+ val ref1 = checkStable(typed(ref, EXPRmode | QUALmode | (mode & TYPEPATmode), pt))
+ tree setType (
+ if (stt.isLiteral) ref1.tpe.resultType.asDeclaredSingleton
+ else ref1.tpe.resultType
+ )
case SelectFromTypeTree(qual, selector) =>
val qual1 = typedType(qual, mode)
@@ -755,8 +755,9 @@ trait Trees { self: Universe =>
case class Annotated(annot: Tree, arg: Tree) extends Tree
/** Singleton type, eliminated by RefCheck */
- case class SingletonTypeTree(ref: Tree)
- extends TypTree
+ case class SingletonTypeTree(ref: Tree) extends TypTree {
+ def isLiteral = false
+ }
/** Type selection <qualifier> # <name>, eliminated by RefCheck */
case class SelectFromTypeTree(qualifier: Tree, name: TypeName)
@@ -7,7 +7,7 @@ Changes: Map(trait A -> List(Changed(Definition(A.x))[method x changed from ()T
invalidate B.scala because inherited method changed [Changed(Definition(A.x))[method x changed from ()T to ()T flags: <deferred> <method> <triedcooking>]]
compiling Set(B.scala)
B.scala:2: error: type mismatch;
- found : Int(3)
+ found : 3.type (with underlying type Int)
required: String
def x = 3
^
@@ -7,7 +7,7 @@ Changes: Map(object A -> List(Changed(Definition(A.x))[method x changed from (i:
invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from (i: Function0)Unit to (i: Function0)Unit flags: <method> <triedcooking>]]
compiling Set(B.scala)
B.scala:2: error: type mismatch;
- found : String("3")
+ found : "3".type (with underlying type java.lang.String)
required: () => String
val x = A.x("3")
^
@@ -8,7 +8,7 @@ Changes: Map(class A -> List(Changed(Definition(A.y))[method y changed from (i:
invalidate B.scala because inherited method changed [Changed(Definition(A.y))[method y changed from (i: Int)String to (i: Int)String flags: implicit <method> <triedcooking>]]
compiling Set(B.scala)
B.scala:2: error: type mismatch;
- found : Int(3)
+ found : 3.type (with underlying type Int)
required: String
val x: String = 3
^
@@ -7,7 +7,7 @@ Changes: Map(object A -> List(Added(Definition(A.x)), Changed(Definition(A.x))[v
invalidate B.scala because it references changed definition [Changed(Definition(A.x))[value x changed from (f: String, g: Int)Int to (f: String, g: Int)Int <and> (f: Int, g: Int)Int flags: <method>]]
compiling Set(B.scala)
B.scala:2: error: type mismatch;
- found : Int(5)
+ found : 5.type (with underlying type Int)
required: String
val y = A.x(5)
^
@@ -11,7 +11,7 @@ constrs.scala:12: error: called constructor's definition must precede calling co
def this(x: Boolean) = this(x)
^
constrs.scala:16: error: type mismatch;
- found : Int(1)
+ found : 1.type (with underlying type Int)
required: a
def this() = this(1)
^
@@ -1,5 +1,5 @@
divergent-implicit.scala:4: error: type mismatch;
- found : Int(1)
+ found : 1.type (with underlying type Int)
required: String
val x1: String = 1
^
@@ -7,7 +7,7 @@ gadts1.scala:20: error: class Cell of type Test.Cell does not take type paramete
case Cell[a](x: Int) => c.x = 5
^
gadts1.scala:20: error: type mismatch;
- found : Int(5)
+ found : 5.type (with underlying type Int)
required: a
case Cell[a](x: Int) => c.x = 5
^
@@ -3,7 +3,7 @@ Unspecified value parameter b.
val fac = Fact(1)(2, 3)
^
names-defaults-neg.scala:5: error: type mismatch;
- found : String("#")
+ found : "#".type (with underlying type String)
required: Int
test1(b = 2, a = "#")
^
@@ -3,17 +3,17 @@ nested-fn-print.scala:4: error: only classes can have declared but undefined mem
var x3: Int => Double
^
nested-fn-print.scala:7: error: type mismatch;
- found : String("a")
+ found : "a".type (with underlying type String)
required: Int => (Float => Double)
x1 = "a"
^
nested-fn-print.scala:8: error: type mismatch;
- found : String("b")
+ found : "b".type (with underlying type String)
required: (Int => Float) => Double
x2 = "b"
^
nested-fn-print.scala:9: error: type mismatch;
- found : String("c")
+ found : "c".type (with underlying type String)
required: Int => Double
x3 = "c"
^
@@ -1,5 +1,5 @@
no-predef.scala:2: error: type mismatch;
- found : scala.Long(5L)
+ found : 5L.type (with underlying type scala.Long)
required: java.lang.Long
def f1 = 5L: java.lang.Long
^
@@ -1,10 +1,10 @@
sensitive2.scala:6: error: type mismatch;
- found : String("abc")
+ found : "abc".type (with underlying type String)
required: Test.Foo[_]
Note that implicit conversions are not applicable because they are ambiguous:
both method foo1 in object Test of type [A](a: A)Test.Foo[A]
and method foo2 in object Test of type (a: Any)Test.Foo[String]
- are possible conversion functions from String("abc") to Test.Foo[_]
+ are possible conversion functions from "abc".type to Test.Foo[_]
val a: Foo[_] = "abc"
^
one error found
@@ -1,5 +1,5 @@
t0590.scala:2: error: type mismatch;
- found : Null(null)
+ found : null.type (with underlying type Null)
required: T
implicit def foo[T] : T = null
^
@@ -1,5 +1,5 @@
t1041.scala:3: error: type mismatch;
- found : Int(1)
+ found : 1.type (with underlying type Int)
required: List[Int]
case 1 => 4
^
@@ -1,5 +1,5 @@
t2139.scala:13: error: type mismatch;
- found : Int(4)
+ found : 4.type (with underlying type Int)
required: Nothing
val z:Int=(u.f _)(4)
^
@@ -1,5 +1,5 @@
t2801.scala:2: error: type mismatch;
- found : Null(null)
+ found : null.type (with underlying type Null)
required: A
def f[A <: AnyRef] = { val a: A = null ; a }
^
@@ -1,5 +1,5 @@
t3006.scala:8: error: type mismatch;
- found : String("H")
+ found : "H".type (with underlying type String)
required: Int
println(A(3) + "H")
^
@@ -4,10 +4,13 @@ t391.scala:2: error: identifier expected but 'def' found.
t391.scala:4: error: ':' expected but '}' found.
}
^
+t391.scala:6: error: '.' expected but ';' found.
+class E(def x: Int); // the "def x" is illegal
+^
t391.scala:6: error: identifier expected but 'def' found.
class E(def x: Int); // the "def x" is illegal
^
t391.scala:6: error: ':' expected but eof found.
class E(def x: Int); // the "def x" is illegal
^
-four errors found
+5 errors found
@@ -1,19 +1,19 @@
t4158.scala:3: error: type mismatch;
- found : Null(null)
+ found : null.type (with underlying type Null)
required: Int
Note that implicit conversions are not applicable because they are ambiguous:
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: Null)Int
and method Integer2int in object Predef of type (x: Integer)Int
- are possible conversion functions from Null(null) to Int
+ are possible conversion functions from null.type to Int
var y = null: Int
^
t4158.scala:2: error: type mismatch;
- found : Null(null)
+ found : null.type (with underlying type Null)
required: Int
Note that implicit conversions are not applicable because they are ambiguous:
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: Null)Int
and method Integer2int in object Predef of type (x: Integer)Int
- are possible conversion functions from Null(null) to Int
+ are possible conversion functions from null.type to Int
var x: Int = null
^
two errors found
@@ -1,5 +1,5 @@
t846.scala:9: error: type mismatch;
- found : Null(null)
+ found : null.type (with underlying type Null)
required: B
if (a != null) f(a) else null
^
@@ -1,5 +1,5 @@
t909.scala:6: error: type mismatch;
- found : String("Hello")
+ found : "Hello".type (with underlying type String)
required: Int
case Foo("Hello") =>
^
Oops, something went wrong.

0 comments on commit 824ce5e

Please sign in to comment.