Skip to content

Unit-test LUBs. #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bincompat-backward.whitelist.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
12 changes: 12 additions & 0 deletions bincompat-forward.whitelist.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
33 changes: 29 additions & 4 deletions spec/03-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ chapter: 3
| SimpleType ‘#’ id
| StableId
| Path ‘.’ ‘type’
| Literal
| ‘(’ Types ‘)’
TypeArgs ::= ‘[’ Types ‘]’
Types ::= Type {‘,’ Type}
Expand Down Expand Up @@ -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`.
<!-- a pattern match/type test against a singleton type `p.type` desugars to `_ eq p` -->

### 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`.

<!-- TODO: use eq when we lift it up to Any -->

<!-- TODO: relate to constant types, which trigger constant folding
ConstantType(1).deconst =:= LiteralType(1)
LiteralType(1).widen =:= IntClass.tpe
-->


### 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

Expand Down
4 changes: 2 additions & 2 deletions spec/06-expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 5 additions & 3 deletions spec/08-pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -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$). <!-- SIP-23 -->

* 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$.
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
17 changes: 14 additions & 3 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ self =>
}
private def inScalaRootPackage = inScalaPackage && currentPackage == "scala"


def parseStartRule: () => Tree

def parseRule[T](rule: this.type => T): T = {
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -930,6 +931,7 @@ self =>
* | SimpleType `#' Id
* | StableId
* | Path `.' type
* | Literal
* | `(' Types `)'
* | WildcardType
* }}}
Expand All @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
41 changes: 30 additions & 11 deletions src/compiler/scala/tools/nsc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/UnCurry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
Loading