Skip to content

Commit

Permalink
Literal-based singleton types. This means things like:
Browse files Browse the repository at this point in the history
  var x: 3.type = 3
  x = 4 // error

All tests pass here after indiscriminately updating various checkfiles.
  • Loading branch information
paulp committed Jul 8, 2012
1 parent 85cd96d commit 824ce5e
Show file tree
Hide file tree
Showing 29 changed files with 127 additions and 44 deletions.
17 changes: 14 additions & 3 deletions src/compiler/scala/reflect/internal/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 8 additions & 4 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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]] = {
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ")
Expand Down
8 changes: 6 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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)
}

Expand Down Expand Up @@ -1034,6 +1037,7 @@ trait Namers extends MethodSynthesis {
rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List()))
else rt
})

}

/**
Expand Down
11 changes: 7 additions & 4 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions src/library/scala/reflect/api/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion test/files/buildmanager/t2651_4/t2651_4.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
^
2 changes: 1 addition & 1 deletion test/files/buildmanager/t2655/t2655.check
Original file line number Diff line number Diff line change
Expand Up @@ -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")
^
2 changes: 1 addition & 1 deletion test/files/buildmanager/t2657/t2657.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
^
2 changes: 1 addition & 1 deletion test/files/buildmanager/t2790/t2790.check
Original file line number Diff line number Diff line change
Expand Up @@ -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)
^
2 changes: 1 addition & 1 deletion test/files/neg/constrs.check
Original file line number Diff line number Diff line change
Expand Up @@ -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)
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/divergent-implicit.check
Original file line number Diff line number Diff line change
@@ -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
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/gadts1.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/names-defaults-neg.check
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "#")
^
Expand Down
6 changes: 3 additions & 3 deletions test/files/neg/nested-fn-print.check
Original file line number Diff line number Diff line change
Expand Up @@ -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"
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/no-predef.check
Original file line number Diff line number Diff line change
@@ -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
^
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/sensitive2.check
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/files/neg/t0590.check
Original file line number Diff line number Diff line change
@@ -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
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t1041.check
Original file line number Diff line number Diff line change
@@ -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
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t2139.check
Original file line number Diff line number Diff line change
@@ -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)
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t2801.check
Original file line number Diff line number Diff line change
@@ -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 }
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t3006.check
Original file line number Diff line number Diff line change
@@ -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")
^
Expand Down
5 changes: 4 additions & 1 deletion test/files/neg/t391.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions test/files/neg/t4158.check
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/files/neg/t846.check
Original file line number Diff line number Diff line change
@@ -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
^
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t909.check
Original file line number Diff line number Diff line change
@@ -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") =>
^
Expand Down
Loading

0 comments on commit 824ce5e

Please sign in to comment.