diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index 076b9bb9aaeb..1d26dce74074 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -198,6 +198,10 @@ filter {
{
matchName="scala.collection.immutable.Stream.scala$collection$immutable$Stream$$loop$4"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.api.Types.LiteralType"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index 53401eefad19..a4dee7261dda 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -292,6 +292,18 @@ filter {
{
matchName="scala.collection.immutable.Stream.scala$collection$immutable$Stream$$loop$2"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.api.Types$LiteralTypeApi"
+ problemName=MissingClassProblem
+ },
+ {
+ matchName="scala.reflect.api.Types.LiteralType"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.api.Types$LiteralTypeExtractor"
+ problemName=MissingClassProblem
}
]
}
diff --git a/spec/03-types.md b/spec/03-types.md
index d067d45ab2fd..c3fa08091b8e 100644
--- a/spec/03-types.md
+++ b/spec/03-types.md
@@ -23,6 +23,7 @@ chapter: 3
| SimpleType ‘#’ id
| StableId
| Path ‘.’ ‘type’
+ | Literal
| ‘(’ Types ‘)’
TypeArgs ::= ‘[’ Types ‘]’
Types ::= Type {‘,’ Type}
@@ -102,16 +103,40 @@ forms.
### Singleton Types
```ebnf
-SimpleType ::= Path ‘.’ type
+SimpleType ::= Path ‘.’ ‘type’
```
A singleton type is of the form $p.$`type`, where $p$ is a
path pointing to a value expected to [conform](06-expressions.html#expression-typing)
to `scala.AnyRef`. The type denotes the set of values
-consisting of `null` and the value denoted by $p$.
+consisting of `null` and the value denoted by $p$
+(i.e., the value $v$ for which `v eq p`).
-A _stable type_ is either a singleton type or a type which is
-declared to be a subtype of trait `scala.Singleton`.
+
+
+### Literal Types
+
+```ebnf
+SimpleType ::= Literal
+```
+
+A literal type `lit` is a special kind of singleton type which denotes the single literal value `lit`.
+Thus, the type ascription `1: 1` gives the most precise type to the literal value `1`: the literal type `1`.
+
+At run time, an expression `e` is considered to have literal type `lit` if `e == lit`.
+Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ : lit => }` is determined by evaluating `e == lit`.
+
+
+
+
+
+
+### Stable Types
+A _stable type_ is a singleton type, a literal type,
+or a type that is declared to be a subtype of trait `scala.Singleton`.
### Type Projection
diff --git a/spec/06-expressions.md b/spec/06-expressions.md
index afd149274413..68f41a6faa8b 100644
--- a/spec/06-expressions.md
+++ b/spec/06-expressions.md
@@ -76,8 +76,8 @@ $T$ forSome { type $t_1[\mathit{tps}\_1] >: L_1 <: U_1$; $\ldots$; type $t_n[\ma
SimpleExpr ::= Literal
```
-Typing of literals is as described [here](01-lexical-syntax.html#literals); their
-evaluation is immediate.
+Typing of literals is described along with their [lexical syntax](01-lexical-syntax.html#literals);
+their evaluation is immediate.
## The _Null_ Value
diff --git a/spec/08-pattern-matching.md b/spec/08-pattern-matching.md
index 3538457b5c36..a0e40be8b0c3 100644
--- a/spec/08-pattern-matching.md
+++ b/spec/08-pattern-matching.md
@@ -337,9 +337,11 @@ A type pattern $T$ is of one of the following forms:
be used as type patterns, because they would match nothing in any case.
* A singleton type `$p$.type`. This type pattern matches only the value
- denoted by the path $p$ (that is, a pattern match involved a
- comparison of the matched value with $p$ using method `eq` in class
- `AnyRef`).
+ denoted by the path $p$ (the `eq` method is used to compare the matched value to $p$).
+
+* A literal type `$lit$`. This type pattern matches only the value
+ denoted by the literal $lit$ (the `==` method is used to compare the matched value to $lit$).
+
* A compound type pattern `$T_1$ with $\ldots$ with $T_n$` where each $T_i$ is a
type pattern. This type pattern matches all values that are matched by each of
the type patterns $T_i$.
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 430424d0f86d..9d9db6f9791a 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -78,6 +78,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
override def settings = currentSettings
+ // TODO: temporary flag to easily enable/disable SIP-23 (aka the type formerly known as 42.type)
+ override def sip23: Boolean = true //settings.Xexperimental.value
+
/** Switch to turn on detailed type logs */
var printTypings = settings.Ytyperdebug.value
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 466381000376..d71a55bc0ed3 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -339,6 +339,7 @@ self =>
}
private def inScalaRootPackage = inScalaPackage && currentPackage == "scala"
+
def parseStartRule: () => Tree
def parseRule[T](rule: this.type => T): T = {
@@ -674,11 +675,11 @@ self =>
def isExprIntro: Boolean = isExprIntroToken(in.token)
- def isTypeIntroToken(token: Token): Boolean = token match {
+ def isTypeIntroToken(token: Token): Boolean = (sip23 && isLiteralToken(token)) || (token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS |
SUPER | USCORE | LPAREN | AT => true
case _ => false
- }
+ })
def isStatSeqEnd = in.token == RBRACE || in.token == EOF
@@ -930,6 +931,7 @@ self =>
* | SimpleType `#' Id
* | StableId
* | Path `.' type
+ * | Literal
* | `(' Types `)'
* | WildcardType
* }}}
@@ -939,7 +941,8 @@ self =>
simpleTypeRest(in.token match {
case LPAREN => atPos(start)(makeTupleType(inParens(types())))
case USCORE => wildcardType(in.skipToken())
- case _ =>
+ case tok if sip23 && isLiteralToken(tok) => atPos(start){SingletonTypeTree(literal())} // SIP-23
+ case _ =>
path(thisOK = false, typeOK = true) match {
case r @ SingletonTypeTree(_) => r
case r => convertToTypeId(r)
@@ -1020,7 +1023,15 @@ self =>
else
mkOp(infixType(InfixMode.RightOp))
}
+ // SIP-23
+ def isNegatedLiteralType = sip23 && (
+ t match { // the token for `t` (Ident("-")) has already been read, thus `isLiteral` below is looking at next token (must be a literal)
+ case Ident(name) if isLiteral => name == nme.MINUS.toTypeName // TODO: OPT? lift out nme.MINUS.toTypeName?
+ case _ => false
+ }
+ )
if (isIdent) checkRepeatedParam orElse asInfix
+ else if (isNegatedLiteralType) atPos(t.pos.start){SingletonTypeTree(literal(isNegated = true, start = t.pos.start))}
else t
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index a6d0d3b9fa15..9fc87d6f8ea2 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -383,6 +383,7 @@ trait TypeKinds { self: ICodes =>
case ThisType(ArrayClass) => ObjectReference
case ThisType(sym) => REFERENCE(sym)
case SingleType(_, sym) => primitiveOrRefType(sym)
+ case LiteralType(_) => toTypeKind(t.underlying)
case ConstantType(_) => toTypeKind(t.underlying)
case TypeRef(_, sym, args) => primitiveOrClassType(sym, args)
case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!")
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 806d4b277ce7..e3ad474edddc 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -435,6 +435,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
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
case ThisType(sym) => getClassBTypeAndRegisterInnerClass(sym)
case SingleType(_, sym) => primitiveOrClassToBType(sym)
+ case LiteralType(_) => toTypeKind(t.underlying)
case ConstantType(_) => toTypeKind(t.underlying)
case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index 25e13a131485..510427c6bb80 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -223,6 +223,8 @@ abstract class Pickler extends SubComponent {
case SingleType(pre, sym) =>
putType(pre)
putSymbol(sym)
+ case LiteralType(value) =>
+ putConstant(value)
case SuperType(thistpe, supertpe) =>
putType(thistpe)
putType(supertpe)
@@ -452,6 +454,7 @@ abstract class Pickler extends SubComponent {
case NoType | NoPrefix =>
case ThisType(sym) => writeRef(sym)
case SingleType(pre, sym) => writeRef(pre) ; writeRef(sym)
+ case LiteralType(value) => writeRef(value)
case SuperType(thistpe, supertpe) => writeRef(thistpe) ; writeRef(supertpe)
case ConstantType(value) => writeRef(value)
case TypeBounds(lo, hi) => writeRef(lo) ; writeRef(hi)
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index b6af19250ecb..0d8f0650c9eb 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -869,6 +869,18 @@ abstract class Erasure extends AddInterfaces
* - Reset all other type attributes to null, thus enforcing a retyping.
*/
private val preTransformer = new TypingTransformer(unit) {
+ // TODO: since the spec defines instanceOf checks in terms of pattern matching,
+ // this extractor should share code with TypeTestTreeMaker
+ object SingletonInstanceCheck {
+ def unapply(pt: Type): Option[(TermSymbol, Tree)] = pt match {
+ case SingleType(_, _) | LiteralType(_) | ThisType(_) | SuperType(_, _) =>
+ val cmpOp = if (pt <:< AnyValTpe) Any_equals else Object_eq
+ val cmpArg = gen.mkAttributedQualifier(pt)
+ Some((cmpOp, cmpArg))
+ case _ =>
+ None
+ }
+ }
private def preEraseNormalApply(tree: Apply) = {
val fn = tree.fun
@@ -881,12 +893,22 @@ abstract class Erasure extends AddInterfaces
def preEraseAsInstanceOf = {
(fn: @unchecked) match {
case TypeApply(Select(qual, _), List(targ)) =>
- if (qual.tpe <:< targ.tpe)
- atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
- else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol))
- atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol))
- else
- tree
+ targ.tpe match {
+ case argTp@SingletonInstanceCheck(cmpOp, cmpArg) =>
+ atPos(tree.pos) {
+ gen.evalOnce(qual, currentOwner, currentUnit) { qual =>
+ If(Apply(Select(qual(), cmpOp), List(cmpArg)),
+ Typed(qual(), TypeTree(argTp)),
+ Throw(ClassCastExceptionClass.tpe_*))
+ }
+ }
+ case argTp if qual.tpe <:< argTp =>
+ atPos(tree.pos) { Typed(qual, TypeTree(argTp)) }
+ case argTp if isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(argTp.typeSymbol) =>
+ atPos(tree.pos)(numericConversion(qual, argTp.typeSymbol))
+ case _ =>
+ tree
+ }
}
// todo: also handle the case where the singleton type is buried in a compound
}
@@ -904,11 +926,8 @@ abstract class Erasure extends AddInterfaces
List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
List()) setPos tree.pos
targ.tpe match {
- case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
- val cmpOp = if (targ.tpe <:< AnyValTpe) Any_equals else Object_eq
- atPos(tree.pos) {
- Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
- }
+ case SingletonInstanceCheck(cmpOp, cmpArg) =>
+ atPos(tree.pos) { Apply(Select(qual, cmpOp), List(cmpArg)) }
case RefinedType(parents, decls) if (parents.length >= 2) =>
gen.evalOnce(qual, currentOwner, unit) { q =>
// Optimization: don't generate isInstanceOf tests if the static type
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 3544dc9966d9..19b2d4a66d2b 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -322,7 +322,7 @@ abstract class UnCurry extends InfoTransform
args.take(formals.length - 1) :+ (suffix setType formals.last)
}
- val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args
+ val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head.widen) else args
map2(formals, args1) { (formal, arg) =>
if (!isByNameParamType(formal))
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 3abec521dfe2..634024ba881a 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -447,7 +447,16 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
// - Scala's arrays are invariant (so we don't drop type tests unsoundly)
if (extractorArgTypeTest) mkDefault
else expectedTp match {
- case SingleType(_, sym) => mkEqTest(gen.mkAttributedQualifier(expectedTp)) // SI-4577, SI-4897
+ case SingleType(_, sym) =>
+ val expected = gen.mkAttributedQualifier(expectedTp)
+ if (expectedTp <:< AnyRefTpe) mkEqTest(expected) // SI-4577, SI-4897
+ else mkEqualsTest(expected)
+ // TODO SIP-23: should we test equality for literal types with eq?
+ // Conceptually cleaner, as SingleType is tested using eq.
+ // In practice it doesn't really matter, since `equals` does the same thing as `eq` in the `AnyVal` subclasses of `Any`.
+ // Should revisit if we end up lifting `eq`'s definition to `Any`, as discussed here:
+ // https://groups.google.com/d/msg/scala-internals/jsVlJI4H5OQ/8emZWRmgzcoJ
+ case LiteralType(const) => mkEqualsTest(expTp(Literal(const)))
case ThisType(sym) if sym.isModule => and(mkEqualsTest(CODE.REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil
case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(CODE.NULL))
case ConstantType(const) => mkEqualsTest(expTp(Literal(const)))
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 74c28122a1a5..74dd2be5807e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -1160,6 +1160,7 @@ trait Implicits {
// necessary only to compile typetags used inside the Universe cake
case ThisType(thisSym) =>
gen.mkAttributedThis(thisSym)
+ // TODO SIP-23: TypeTag for LiteralType
case _ =>
// if `pre` is not a PDT, e.g. if someone wrote
// implicitly[scala.reflect.macros.blackbox.Context#TypeTag[Int]]
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index a1de5e303bc9..e490dd4afd95 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -828,7 +828,28 @@ trait Namers extends MethodSynthesis {
}
}
- /** This method has a big impact on the eventual compiled code.
+ private def refersToSymbolLessAccessibleThan(tp: Type, sym: Symbol): Boolean = {
+ val accessibilityReference =
+ if (sym.isValue && sym.owner.isClass && sym.isPrivate)
+ sym.getterIn(sym.owner)
+ else sym
+
+ @tailrec def loop(tp: Type): Boolean = tp match {
+ case SingleType(pre, sym) =>
+ (sym isLessAccessibleThan accessibilityReference) || loop(pre)
+ case ThisType(sym) =>
+ sym isLessAccessibleThan accessibilityReference
+ case p: SimpleTypeProxy =>
+ loop(p.underlying)
+ case _ =>
+ false
+ }
+
+ loop(tp)
+ }
+
+ /**
+ * This method has a big impact on the eventual compiled code.
* At this point many values have the most specific possible
* type (e.g. in val x = 42, x's type is Int(42), not Int) but
* most need to be widened to avoid undesirable propagation of
@@ -841,35 +862,39 @@ 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 = {
- val getter =
- if (sym.isValue && sym.owner.isClass && sym.isPrivate)
- sym.getter(sym.owner)
- else sym
- def isHidden(tp: Type): Boolean = tp match {
- case SingleType(pre, sym) =>
- (sym isLessAccessibleThan getter) || isHidden(pre)
- case ThisType(sym) =>
- sym isLessAccessibleThan getter
- case p: SimpleTypeProxy =>
- isHidden(p.underlying)
- case _ =>
- false
- }
- val shouldWiden = (
- !tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
- && (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
- && ( sym.isVariable
- || sym.isMethod && !sym.hasAccessorFlag
- || isHidden(tpe)
- )
- )
- dropIllegalStarTypes(
+ private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type =
+ if (sip23) { // SIP-23
+ // TODO: spec -- this is a crucial part of type inference
+ // NOTES:
+ // - Can we widen less? (E.g., for local definitions.)
+ // - Do we need to check tpe.deconst <:< pt?
+ // - We don't need to call dropIllegalStarTypes on a ref to a module class, do we? Where would the stars be? In the prefix?
+
+ // We're inferring the result type of a stable symbol, and the type doesn't refer to a hidden symbol
+ val mayKeepSingletonType = sym.isStable && !refersToSymbolLessAccessibleThan(tpe, sym)
+
+ // (OPT: 99.99% of the time, pt will be WildcardType)
+ @inline def cannotWiden = (pt ne WildcardType) && !(tpe.widen <:< pt)
+
+ // If the definition can keep its inferred singleton type,
+ // or widening would mean no longer conforming to the expected type,
+ // we must still deconst unless it's a final val. Otherwise, widen.
+ if (mayKeepSingletonType || cannotWiden) { if (sym.isFinal) tpe else tpe.deconst }
+ else tpe.widen
+ } else {
+ val shouldWiden = (
+ !tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
+ && (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
+ && ( sym.isVariable
+ || sym.isMethod && !sym.hasAccessorFlag
+ || refersToSymbolLessAccessibleThan(tpe, sym)
+ )
+ )
if (shouldWiden) tpe.widen
else if (sym.isFinal) tpe // "final val" allowed to retain constant type
else tpe.deconst
- )
- }
+ }
+
/** Computes the type of the body in a ValDef or DefDef, and
* assigns the type to the tpt's node. Returns the type.
*/
@@ -879,7 +904,7 @@ trait Namers extends MethodSynthesis {
case _ => defnTyper.computeType(tree.rhs, pt)
}
- val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt)
+ val defnTpe = dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt))
tree.tpt defineType defnTpe setPos tree.pos.focus
tree.tpt.tpe
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 4d9a6a47ef56..f5553919b7f8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -5178,15 +5178,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
def typedSingletonTypeTree(tree: SingletonTypeTree) = {
+ // SIP-23: don't require AnyRef for 1.type etc
+ val pt = if (sip23) WildcardType else AnyRefTpe
val refTyped =
context.withImplicitsDisabled {
- typed(tree.ref, MonoQualifierModes | mode.onlyTypePat, AnyRefTpe)
+ typed(tree.ref, MonoQualifierModes | mode.onlyTypePat, pt)
}
if (refTyped.isErrorTyped) {
setError(tree)
} else {
- tree setType refTyped.tpe.resultType
+ // .resultType unwraps NullaryMethodType (accessor of a path)
+ // .deconst unwraps the ConstantType to a LiteralType (for literal-based singleton types)
+ tree setType refTyped.tpe.resultType.deconst
if (refTyped.isErrorTyped || treeInfo.admitsTypeSelection(refTyped)) tree
else UnstableTreeError(tree)
}
diff --git a/src/eclipse/partest/.classpath b/src/eclipse/partest/.classpath
index 7f28868d95ee..d9294625b0f6 100644
--- a/src/eclipse/partest/.classpath
+++ b/src/eclipse/partest/.classpath
@@ -5,7 +5,7 @@
-
+
diff --git a/src/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath
index c8f0e89b8a8c..fad852db5a35 100644
--- a/src/eclipse/scaladoc/.classpath
+++ b/src/eclipse/scaladoc/.classpath
@@ -6,8 +6,8 @@
-
-
-
+
+
+
diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala
index f6995dd5dea7..53c0e27c336d 100644
--- a/src/reflect/scala/reflect/api/Types.scala
+++ b/src/reflect/scala/reflect/api/Types.scala
@@ -528,12 +528,17 @@ trait Types {
*/
def supertpe: Type
}
- /** The `ConstantType` type is not directly written in user programs, but arises as the type of a constant.
- * The REPL expresses constant types like `Int(11)`. Here are some constants with their types:
+
+ /** A `ConstantType` type cannot be expressed in user programs; it is inferred as the type of a constant.
+ * Here are some constants with their types and the internal string representation:
* {{{
- * 1 ConstantType(Constant(1))
- * "abc" ConstantType(Constant("abc"))
+ * 1 ConstantType(Constant(1)) Int(1)
+ * "abc" ConstantType(Constant("abc")) String("abc")
* }}}
+ *
+ * ConstantTypes denote values that may safely be constant folded during type checking.
+ * The `deconst` operation returns the equivalent type that will not be constant folded.
+ *
* @template
* @group Types
*/
@@ -565,6 +570,36 @@ trait Types {
def value: Constant
}
+ /** The `LiteralType` type represent a singleton type with a literal for its path.
+ * Examples: "a".type or 42.type, which may be abbreviated to "a" or 42.
+ *
+ * @template
+ * @group Types
+ */
+ type LiteralType >: Null <: LiteralTypeApi with SingletonType
+
+ /** The constructor/extractor for `ConstantType` instances.
+ * @group Extractors
+ */
+ val LiteralType: LiteralTypeExtractor
+
+ /** An extractor class to create and pattern match with syntax `LiteralType(constant)`
+ * Here, `constant` is the constant value represented by the type.
+ * @group Extractors
+ */
+ abstract class LiteralTypeExtractor {
+ def unapply(tpe: LiteralType): Option[Constant]
+ }
+
+ /** The API that all literal types support.
+ * The main source of information about types is the [[scala.reflect.api.Types]] page.
+ * @group API
+ */
+ trait LiteralTypeApi extends TypeApi { this: LiteralType =>
+ /** The compile-time constant underlying this type. */
+ def value: Constant
+ }
+
/** The `TypeRef` type describes types of any of the forms on the left,
* with their TypeRef representations to the right.
* {{{
diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala
index 85d0efdcba79..904b30326b8f 100644
--- a/src/reflect/scala/reflect/internal/Constants.scala
+++ b/src/reflect/scala/reflect/internal/Constants.scala
@@ -60,6 +60,7 @@ trait Constants extends api.Constants {
def isFloatRange: Boolean = ByteTag <= tag && tag <= FloatTag
def isNumeric: Boolean = ByteTag <= tag && tag <= DoubleTag
def isNonUnitAnyVal = BooleanTag <= tag && tag <= DoubleTag
+ def isSuitableLiteralType = BooleanTag <= tag && tag <= NullTag
def isAnyVal = UnitTag <= tag && tag <= DoubleTag
def tpe: Type = tag match {
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 7e2d1244864d..3ea8a7c83b0d 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -1357,7 +1357,7 @@ trait Definitions extends api.StandardDefinitions {
else boxedClass.map(kvp => (kvp._2: Symbol, kvp._1)).getOrElse(sym, NoSymbol)
/** Is type's symbol a numeric value class? */
- def isNumericValueType(tp: Type): Boolean = tp match {
+ def isNumericValueType(tp: Type): Boolean = tp.widen match {
case TypeRef(_, sym, _) => isNumericValueClass(sym)
case _ => false
}
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index ed5c68fe82f7..0f85d63511b7 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -52,6 +52,7 @@ abstract class SymbolTable extends macros.Universe
val gen = new InternalTreeGen { val global: SymbolTable.this.type = SymbolTable.this }
def log(msg: => AnyRef): Unit
+ def sip23: Boolean = false // temporary, overriden in Global
protected def elapsedMessage(msg: String, start: Long) =
msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start) + "ms"
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index 6e8e992d1656..8f8de60c1758 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -77,6 +77,8 @@ abstract class TreeGen {
else mkAttributedThis(clazz)
case SingleType(pre, sym) =>
mkApplyIfNeeded(mkAttributedStableRef(pre, sym))
+ case LiteralType(value) =>
+ Literal(value) setType tpe
case TypeRef(pre, sym, args) =>
if (sym.isRoot) {
mkAttributedThis(sym)
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index a95f626a0b5e..15e302fcbdf1 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -34,6 +34,8 @@ import TypeConstants._
// super references
case SingleType(pre, sym) =>
// pre.sym.type
+ case LiteralType(value) =>
+ // value.type
case ConstantType(value) =>
// Int(2)
case TypeRef(pre, sym, args) =>
@@ -1263,6 +1265,28 @@ trait Types
}
}
+ /** A class representing a literal type. Conceptually, a SingleType where the path is a literal.
+ *
+ * Along with ConstantType (which triggers constant folding), the most precise type for a literal.
+ *
+ * This type is created by `deconst`ing the corresponding `ConstantType` (see typedLiteral and widenIfNecessary);
+ * it `widen`s to the type of `value`.
+ *
+ * NOTE: `()` is not a literal, and neither are `Symbol`s (`'a`)
+ */
+ abstract case class LiteralType(value: Constant) extends SingletonType with LiteralTypeApi {
+ override def underlying: Type = value.tpe
+ override def isTrivial: Boolean = true
+ override def safeToString: String = value.escapedStringValue
+ override def kind = "LiteralType"
+ }
+
+ final class UniqueLiteralType(value: Constant) extends LiteralType(value)
+
+ object LiteralType extends LiteralTypeExtractor {
+ def apply(value: Constant) = unique(new UniqueLiteralType(value))
+ }
+
abstract case class SuperType(thistpe: Type, supertpe: Type) extends SingletonType with SuperTypeApi {
private var trivial: ThreeValue = UNKNOWN
override def isTrivial: Boolean = {
@@ -1811,15 +1835,17 @@ trait Types
class PackageClassInfoType(decls: Scope, clazz: Symbol)
extends ClassInfoType(List(), decls, clazz)
- /** A class representing a constant type.
+ /** A class representing a constant type. Constant types are constant-folded during type checking.
+ * To avoid constant folding, use the type returned by `deconst` instead.
*/
abstract case class ConstantType(value: Constant) extends SingletonType with ConstantTypeApi {
- override def underlying: Type = value.tpe
assert(underlying.typeSymbol != UnitClass)
+
+ override def underlying: Type = if (sip23 && value.isSuitableLiteralType) LiteralType(value) else value.tpe // SIP-23
override def isTrivial: Boolean = true
override def deconst: Type = underlying.deconst
override def safeToString: String =
- underlying.toString + "(" + value.escapedStringValue + ")"
+ underlying.widen.toString + "(" + value.escapedStringValue + ")"
override def kind = "ConstantType"
}
@@ -2421,7 +2447,7 @@ trait Types
if (isTrivial || phase.erasedTypes) resultType
else if (/*isDependentMethodType &&*/ sameLength(actuals, params)) {
val idm = new InstantiateDependentMap(params, actuals)
- val res = idm(resultType)
+ val res = idm(resultType).deconst
existentialAbstraction(idm.existentialsNeeded, res)
}
else existentialAbstraction(params, resultType)
@@ -2781,18 +2807,27 @@ trait Types
* See SI-5359.
*/
val bounds = tparam.info.bounds
+
/* We can seed the type constraint with the type parameter
* bounds as long as the types are concrete. This should lower
* the complexity of the search even if it doesn't improve
* any results.
*/
- if (propagateParameterBoundsToTypeVars) {
- val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType)
+ val constr =
+ if (propagateParameterBoundsToTypeVars) {
+ val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType)
- if (exclude) new TypeConstraint
- else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds))
- }
- else new TypeConstraint
+ if (exclude) new TypeConstraint
+ else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds))
+ }
+ else new TypeConstraint
+
+ // TODO: this assumes covariance.. we should look at bounds.lo for contravariance
+ // can we plug this in where variance flipping is dealt with for us already?
+ def precludesWidening(tp: Type) = settings.Xexperimental && (tp.isStable || tp.contains(SingletonClass))
+ if (precludesWidening(bounds.hi)) constr.stopWidening()
+
+ constr
}
def untouchable(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = true)
def apply(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = false)
@@ -3988,7 +4023,7 @@ trait Types
* except it excludes ConstantTypes.
*/
def isSingleType(tp: Type) = tp match {
- case ThisType(_) | SuperType(_, _) | SingleType(_, _) => true
+ case ThisType(_) | SuperType(_, _) | SingleType(_, _) | LiteralType(_) => true
case _ => false
}
diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala
index 12b765b7a60b..9c3754a52e32 100644
--- a/src/reflect/scala/reflect/internal/Variances.scala
+++ b/src/reflect/scala/reflect/internal/Variances.scala
@@ -198,7 +198,8 @@ trait Variances {
def inSym(sym: Symbol): Variance = if (sym.isAliasType) inType(sym.info).cut else inType(sym.info)
def inType(tp: Type): Variance = tp match {
case ErrorType | WildcardType | NoType | NoPrefix => Bivariant
- case ThisType(_) | ConstantType(_) => Bivariant
+ case ThisType(_) | ConstantType(_)
+ | LiteralType(_) => Bivariant
case TypeRef(_, `tparam`, _) => Covariant
case BoundedWildcardType(bounds) => inType(bounds)
case NullaryMethodType(restpe) => inType(restpe)
diff --git a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala
index ce0ceec688dc..7078e637a354 100644
--- a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala
+++ b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala
@@ -131,6 +131,7 @@ object PickleFormat {
final val NOPREFIXtpe = 12
final val THIStpe = 13
final val SINGLEtpe = 14
+ final val LITERALtpe = LITERAL // TODO SIP-23
final val CONSTANTtpe = 15
final val TYPEREFtpe = 16
final val TYPEBOUNDStpe = 17
diff --git a/src/reflect/scala/reflect/internal/pickling/Translations.scala b/src/reflect/scala/reflect/internal/pickling/Translations.scala
index d924cb3a0c7c..4ca42466714c 100644
--- a/src/reflect/scala/reflect/internal/pickling/Translations.scala
+++ b/src/reflect/scala/reflect/internal/pickling/Translations.scala
@@ -66,6 +66,7 @@ trait Translations {
case NoPrefix => NOPREFIXtpe
case _: ThisType => THIStpe
case _: SingleType => SINGLEtpe
+ case _: LiteralType => LITERALtpe
case _: SuperType => SUPERtpe
case _: ConstantType => CONSTANTtpe
case _: TypeBounds => TYPEBOUNDStpe
diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
index 5433bfad6031..bbf9df593f0b 100644
--- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
+++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
@@ -399,6 +399,7 @@ abstract class UnPickler {
case SINGLEtpe => SingleType(readTypeRef(), readSymbolRef())
case SUPERtpe => SuperType(readTypeRef(), readTypeRef())
case CONSTANTtpe => ConstantType(readConstantRef())
+ case LITERALtpe => LiteralType(readConstantRef())
case TYPEREFtpe => TypeRef(readTypeRef(), readSymbolRef(), readTypes())
case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef())
case REFINEDtpe | CLASSINFOtpe => CompoundType(readSymbolRef(), readTypes())
diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
index 123b44aa05cc..1d174a6fa5c7 100644
--- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
+++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
@@ -257,10 +257,13 @@ private[internal] trait GlbLubs {
lub(tps)
)
+ // Need to widen result when using isNumericSubType to compare.
+ // isNumericSubType considers types after dealiasWiden, so should perform same transform on return.
+ // Example unit test: `numericLub(0, 1) == Int` (without the dealiasWiden one of the types would be returned as-is...)
def numericLub(ts: List[Type]) =
ts reduceLeft ((t1, t2) =>
- if (isNumericSubType(t1, t2)) t2
- else if (isNumericSubType(t2, t1)) t1
+ if (isNumericSubType(t1, t2)) t2.dealiasWiden
+ else if (isNumericSubType(t2, t1)) t1.dealiasWiden
else IntTpe)
private val _lubResults = new mutable.HashMap[(Depth, List[Type]), Type]
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
index f9b10c90beb5..3de1656988ae 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
@@ -224,6 +224,7 @@ trait TypeComparers {
case ExistentialType(qs1, res1) => tp2 match { case ExistentialType(qs2, res2) => equalTypeParamsAndResult(qs1, res1, qs2, res2) ; case _ => false }
case ThisType(sym1) => tp2 match { case ThisType(sym2) => sym1 == sym2 ; case _ => false }
case ConstantType(c1) => tp2 match { case ConstantType(c2) => c1 == c2 ; case _ => false }
+ case LiteralType(c1) => tp2 match { case LiteralType(c2) => c1 == c2 ; case _ => false }
case NullaryMethodType(res1) => tp2 match { case NullaryMethodType(res2) => res1 =:= res2 ; case _ => false }
case TypeBounds(lo1, hi1) => tp2 match { case TypeBounds(lo2, hi2) => lo1 =:= lo2 && hi1 =:= hi2 ; case _ => false }
case _ => false
@@ -371,7 +372,8 @@ trait TypeComparers {
private def isSubType2(tp1: Type, tp2: Type, depth: Depth): Boolean = {
def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSubType(lhs, rhs, depth)
- if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2))
+ // TODO SIP-23
+ if (tp1.isInstanceOf[SingletonType] && tp2.isInstanceOf[SingletonType])
return (tp1 =:= tp2) || isThisAndSuperSubtype(tp1, tp2) || retry(tp1.underlying, tp2)
if (tp1.isHigherKinded || tp2.isHigherKinded)
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
index c1c43178e519..9071d6ce5cb5 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala
@@ -93,6 +93,7 @@ private[internal] trait TypeConstraints {
def loBounds: List[Type] = if (numlo == NoType) lobounds else numlo :: lobounds
def hiBounds: List[Type] = if (numhi == NoType) hibounds else numhi :: hibounds
def avoidWiden: Boolean = avoidWidening
+ def stopWidening(): Unit = avoidWidening = true
def addLoBound(tp: Type, isNumericBound: Boolean = false) {
// For some reason which is still a bit fuzzy, we must let Nothing through as
@@ -117,9 +118,9 @@ private[internal] trait TypeConstraints {
}
def checkWidening(tp: Type) {
- if(tp.isStable) avoidWidening = true
+ if(tp.isStable) stopWidening()
else tp match {
- case HasTypeMember(_, _) => avoidWidening = true
+ case HasTypeMember(_, _) => stopWidening()
case _ =>
}
}
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
index c705ca70694d..8ffbab4525a6 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
@@ -124,6 +124,7 @@ private[internal] trait TypeMaps {
if (pre1 eq pre) tp
else singleType(pre1, sym)
}
+ case LiteralType(_) => tp
case MethodType(params, result) =>
val params1 = flipped(mapOver(params))
val result1 = this(result)
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index c87b810bdd31..a8f42fdaf043 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -137,6 +137,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.NoPrefix
this.ThisType
this.SingleType
+ this.LiteralType
this.SuperType
this.TypeBounds
this.CompoundType
diff --git a/test/files/pos/sip23_numericLub.scala b/test/files/pos/sip23_numericLub.scala
new file mode 100644
index 000000000000..b156a81ed083
--- /dev/null
+++ b/test/files/pos/sip23_numericLub.scala
@@ -0,0 +1,3 @@
+class C {
+ def foo(x: Boolean) = if (x) 1 else 0
+}
\ No newline at end of file
diff --git a/test/files/run/literal-type-varargs.flags b/test/files/run/literal-type-varargs.flags
new file mode 100644
index 000000000000..48fd867160ba
--- /dev/null
+++ b/test/files/run/literal-type-varargs.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/literal-type-varargs.scala b/test/files/run/literal-type-varargs.scala
new file mode 100644
index 000000000000..3b78af727dc1
--- /dev/null
+++ b/test/files/run/literal-type-varargs.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ val x = List.apply[1](1)
+ assert(x == List(1))
+}
diff --git a/test/files/run/sip-23-implicit-resolution.flags b/test/files/run/sip-23-implicit-resolution.flags
new file mode 100644
index 000000000000..48fd867160ba
--- /dev/null
+++ b/test/files/run/sip-23-implicit-resolution.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/sip-23-implicit-resolution.scala b/test/files/run/sip-23-implicit-resolution.scala
new file mode 100644
index 000000000000..5d83f89c43b1
--- /dev/null
+++ b/test/files/run/sip-23-implicit-resolution.scala
@@ -0,0 +1,17 @@
+object Test extends App {
+ trait Assoc[K] { type V ; val v: V }
+
+ def mkAssoc[K, V0](k: K, v0: V0): Assoc[k.type] { type V = V0 } = new Assoc[k.type] {type V = V0 ; val v = v0}
+ def lookup[K](k: K)(implicit a: Assoc[k.type]): a.V = a.v
+
+ implicit def firstAssoc = mkAssoc(1, "Panda!")
+ implicit def secondAssoc = mkAssoc(2, "Kitty!")
+
+ implicit def ageAssoc = mkAssoc("Age", 3)
+ implicit def nmAssoc = mkAssoc("Name", "Jane")
+
+ assert(lookup(1) == "Panda!")
+ assert(lookup(2) == "Kitty!")
+ assert(lookup("Age") == 3)
+ assert(lookup("Name") == "Jane")
+}
diff --git a/test/files/run/sip-23-no-constant-folding.check b/test/files/run/sip-23-no-constant-folding.check
new file mode 100644
index 000000000000..c6a6bd717378
--- /dev/null
+++ b/test/files/run/sip-23-no-constant-folding.check
@@ -0,0 +1,2 @@
+got 42
+panda
diff --git a/test/files/run/sip-23-no-constant-folding.flags b/test/files/run/sip-23-no-constant-folding.flags
new file mode 100644
index 000000000000..48fd867160ba
--- /dev/null
+++ b/test/files/run/sip-23-no-constant-folding.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/sip-23-no-constant-folding.scala b/test/files/run/sip-23-no-constant-folding.scala
new file mode 100644
index 000000000000..17cc9f1d8913
--- /dev/null
+++ b/test/files/run/sip-23-no-constant-folding.scala
@@ -0,0 +1,15 @@
+object Test extends App {
+
+ def noisyId[A](x: A): x.type = {
+ println(s"got $x")
+ x
+ }
+
+ def testNoConstantFolding(): 23 = {
+ println("panda")
+ 23
+ }
+
+ assert(noisyId(42) == 42)
+ assert(testNoConstantFolding == 23)
+}
diff --git a/test/files/run/sip-23-type-equality.flags b/test/files/run/sip-23-type-equality.flags
new file mode 100644
index 000000000000..48fd867160ba
--- /dev/null
+++ b/test/files/run/sip-23-type-equality.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/sip-23-type-equality.scala b/test/files/run/sip-23-type-equality.scala
new file mode 100644
index 000000000000..f9052323ea34
--- /dev/null
+++ b/test/files/run/sip-23-type-equality.scala
@@ -0,0 +1,8 @@
+object Test extends App {
+ val x = 1
+ val y: x.type = 1
+
+ implicitly[x.type =:= y.type]
+ implicitly[1 =:= y.type]
+ implicitly[1 =:= x.type]
+}
diff --git a/test/junit/scala/reflect/internal/TypesTest.scala b/test/junit/scala/reflect/internal/TypesTest.scala
index 95194ef0a453..d27fe4178e6b 100644
--- a/test/junit/scala/reflect/internal/TypesTest.scala
+++ b/test/junit/scala/reflect/internal/TypesTest.scala
@@ -32,4 +32,45 @@ class TypesTest {
val uniquelyNarrowed2 = refinedType(boolWithString1narrow2 :: Nil, NoSymbol)
assert(uniquelyNarrowed1 =:= uniquelyNarrowed2)
}
+
+ @Test
+ def testSameTypesLub(): Unit = {
+ def testSameType(tpe: Type, num: Int = 5) = assert(lub(List.fill(num)(tpe)) =:= tpe)
+
+ testSameType(IntTpe)
+ testSameType(StringTpe)
+ testSameType(typeOf[Class[String]])
+ testSameType(LiteralType(Constant(1)))
+ testSameType(LiteralType(Constant("test")))
+ }
+
+ @Test
+ def testTypesLub(): Unit = {
+ val interestingCombos: Map[Type, List[List[Type]]] = Map(
+ IntTpe -> List(List(LiteralType(Constant(0)), LiteralType(Constant(1))),
+ List(LiteralType(Constant(0)), IntTpe),
+ List(ConstantType(Constant(0)), IntTpe),
+ List(ConstantType(Constant(0)), LiteralType(Constant(1))),
+ List(ConstantType(Constant(1)), LiteralType(Constant(1))), // Should this be LiteralType(Constant(1))?
+
+ List(LiteralType(Constant(1)), ConstantType(Constant(1))), // Same here
+ List(LiteralType(Constant(0)), ConstantType(Constant(1)))),
+ StringTpe -> List(List(LiteralType(Constant("a")), LiteralType(Constant("b"))),
+ List(LiteralType(Constant("a")), StringTpe),
+ List(ConstantType(Constant("a")), LiteralType(Constant("a"))), // Should this be LiteralType(Constant("a"))?
+ List(LiteralType(Constant("a")), ConstantType(Constant("a"))), // Same here
+ List(ConstantType(Constant("a")), StringTpe),
+ List(ConstantType(Constant("a")), LiteralType(Constant("b"))),
+ List(ConstantType(Constant("a")), LiteralType(Constant("b")))),
+ LiteralType(Constant(1)) -> List(List(LiteralType(Constant(1)), LiteralType(Constant(1)))),
+ LiteralType(Constant("a")) -> List(List(LiteralType(Constant("a")), LiteralType(Constant("a")))),
+ AnyValTpe -> List(List(LiteralType(Constant(1)), IntTpe, DoubleTpe)),
+ typeOf[Class[String]] -> List(List(typeOf[Class[String]], typeOf[Class[String]])),
+ typeOf[Class[_ >: String <: Object]] -> List(List(typeOf[Class[String]], typeOf[Class[Object]]))
+ )
+
+ interestingCombos foreach { case (result, checks) =>
+ checks.foreach(check => assert(lub(check) =:= result))
+ }
+ }
}