Skip to content

Change is volatile 2 #1072

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

Merged
merged 27 commits into from
Feb 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d872d9c
Remove unnecessary logic in toBounds
odersky Jan 4, 2016
91ccb52
Support named type parameters
odersky Jan 7, 2016
5a8e870
First version of named type arguments
odersky Jan 9, 2016
3363c4e
Make some operations surivive partial named parameter lists
odersky Jan 10, 2016
6947b63
Augment test case
odersky Jan 10, 2016
b79c5b4
Make named parameter alias handling more robust
odersky Jan 10, 2016
ba91b76
Avoid cyclic reference error when building dotty.
odersky Jan 10, 2016
0a95366
Move failing test to pending
odersky Jan 10, 2016
9757adc
Fix pending test for pickling
odersky Jan 11, 2016
5ac739e
Fix PostTyper normalization for named args
odersky Jan 12, 2016
556230a
Check named type params for welformedness rules.
odersky Jan 12, 2016
dbf04a6
Annotate test with // error indications
odersky Jan 12, 2016
70e27f8
More named param tests
odersky Jan 12, 2016
03a8805
Address reviewer comments
odersky Jan 17, 2016
f94c53c
Add doc comment
odersky Jan 18, 2016
d0b1ebc
Make type parameter reordering generally available.
odersky Jan 18, 2016
103339e
Add test to illustrate overloading problem.
odersky Jan 18, 2016
66924f4
Fix assertion error message
odersky Jan 18, 2016
a553296
Fix review comment in previous PR
odersky Jan 18, 2016
6ee8569
Fix problem of overloading resolution when receiver is not stable.
odersky Jan 18, 2016
9dd07aa
Take defult parameters into account for overloading resolution.
odersky Jan 19, 2016
356e59c
Add some flexibility in comparing named and unnamed parameterized types.
odersky Jan 20, 2016
1d585f1
Use hasNamedArgs instead of repeating test inline.
odersky Jan 24, 2016
8441de7
Allow Named Arguments in TypeArgs
odersky Jan 29, 2016
8158279
Simplify logic in matchNamed
odersky Feb 9, 2016
cecaa4b
Remove unused method, fix comments.
odersky Feb 9, 2016
1230158
Fix merge breakage.
odersky Feb 19, 2016
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
6 changes: 4 additions & 2 deletions docs/SyntaxSummary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ grammar.
RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds)
WithType ::= AnnotType {`with' AnnotType} (deprecated)
AnnotType ::= SimpleType {Annotation} Annotated(t, annot)
SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args)
SimpleType ::= SimpleType (TypeArgs | NamedTypeArgs) AppliedTypeTree(t, args)
| SimpleType `#' id SelectFromTypeTree(t, name)
| StableId
| Path `.' `type' SingletonTypeTree(p)
Expand All @@ -118,6 +118,8 @@ grammar.
ParamType ::= [`=>'] ParamValueType
ParamValueType ::= Type [`*'] PostfixOp(t, "*")
TypeArgs ::= `[' ArgTypes `]' ts
NamedTypeArg ::= id `=' ArgType NamedArg(id, t)
NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' nts
Refinement ::= `{' [Dcl] {semi [Dcl]} `}' ds
TypeBounds ::= [`>:' Type] [`<: Type] | INT TypeBoundsTree(lo, hi)
TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} ContextBounds(typeBounds, tps)
Expand Down Expand Up @@ -160,7 +162,7 @@ grammar.
| `_'
| `(' ExprsInParens `)' Parens(exprs)
| SimpleExpr `.' id Select(expr, id)
| SimpleExpr TypeArgs TypeApply(expr, args)
| SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args)
| SimpleExpr1 ArgumentExprs Apply(expr, args)
| XmlExpr
ExprsInParens ::= ExprInParens {`,' ExprInParens}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object desugar {
val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
if (local.exists) (defctx.owner.thisType select local).dealias
else throw new Error(s"no matching symbol for ${sym.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}")
else throw new Error(s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}")
case _ =>
mapOver(tp)
}
Expand Down
4 changes: 4 additions & 0 deletions src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => false
}

/** Does this list contain a named argument tree? */
def hasNamedArg(args: List[Any]) = args exists isNamedArg
val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]]

/** Is this pattern node a catch-all (wildcard or variable) pattern? */
def isDefaultCase(cdef: CaseDef) = cdef match {
case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat)
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
ta.assignType(untpd.Typed(expr, tpt), tpt)

def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) =
def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg =
ta.assignType(untpd.NamedArg(name, arg), arg)

def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
Expand Down
23 changes: 5 additions & 18 deletions src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,6 @@ class CheckRealizable(implicit ctx: Context) {
*/
private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module)

/** Is this type a path with some part that is initialized on use?
*/
private def isLateInitialized(tp: Type): Boolean = tp.dealias match {
case tp: TermRef =>
isLateInitialized(tp.symbol) || isLateInitialized(tp.prefix)
case _: SingletonType | NoPrefix =>
false
case tp: TypeRef =>
true
case tp: TypeProxy =>
isLateInitialized(tp.underlying)
case tp: AndOrType =>
isLateInitialized(tp.tp1) || isLateInitialized(tp.tp2)
case _ =>
true
}

/** The realizability status of given type `tp`*/
def realizability(tp: Type): Realizability = tp.dealias match {
case tp: TermRef =>
Expand Down Expand Up @@ -121,13 +104,17 @@ class CheckRealizable(implicit ctx: Context) {
}
}

/** `Realizable` if `tp` all of `tp`'s non-struct fields have realizable types,
/** `Realizable` if all of `tp`'s non-struct fields have realizable types,
* a `HasProblemField` instance pointing to a bad field otherwise.
*/
private def memberRealizability(tp: Type) = {
def checkField(sofar: Realizability, fld: SingleDenotation): Realizability =
sofar andAlso {
if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy))
// if field is private it cannot be part of a visible path
// if field is mutable it cannot be part of a path
// if field is lazy it does not need to be initialized when the owning object is
// so in all cases the field does not influence realizability of the enclosing object.
Realizable
else {
checkedFields += fld.symbol
Expand Down
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,9 @@ object Flags {
/** A private parameter accessor */
final val PrivateParamAccessor = allOf(Private, ParamAccessor)

/** A type parameter introduced with [type ... ] */
final val NamedTypeParam = allOf(TypeParam, ParamAccessor)

/** A local parameter */
final val ParamAndLocal = allOf(Param, Local)

Expand Down
14 changes: 14 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,20 @@ object SymDenotations {
if (myMemberCache != null) myMemberCache invalidate sym.name
}

/** Make sure the type parameters of this class are `tparams`, reorder definitions
* in scope if necessary.
* @pre All type parameters in `tparams` are entered in class scope `info.decls`.
*/
def updateTypeParams(tparams: List[Symbol])(implicit ctx: Context): Unit =
if (!typeParams.corresponds(tparams)(_.name == _.name)) {
val decls = info.decls
val decls1 = newScope
for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name))
for (sym <- decls) if (!typeParams.contains(sym)) decls1.enter(sym)
info = classInfo.derivedClassInfo(decls = decls1)
myTypeParams = null
}

/** All members of this class that have the given name.
* The elements of the returned pre-denotation all
* have existing symbols.
Expand Down
8 changes: 5 additions & 3 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ class TypeApplications(val self: Type) extends AnyVal {
else tsym.infoOrCompleter match {
case completer: TypeParamsCompleter =>
val tparams = completer.completerTypeParams(tsym)
if (tsym.isClass) tparams
else defn.LambdaTrait(tparams.map(_.variance)).typeParams
defn.LambdaTrait(tparams.map(_.variance)).typeParams
case _ =>
if (!tsym.isCompleting || tsym.isAliasType) tsym.info.typeParams
else
Expand Down Expand Up @@ -548,9 +547,12 @@ class TypeApplications(val self: Type) extends AnyVal {
self
case _ =>
val v = tparam.variance
/* Not neeeded.
if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self)
else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self)
else TypeAlias(self, v)
else
*/
TypeAlias(self, v)
}

/** The type arguments of this type's base type instance wrt. `base`.
Expand Down
41 changes: 38 additions & 3 deletions src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case _ =>
compareRefinedSlow ||
fourthTry(tp1, tp2) ||
compareHkLambda(tp2, tp1, inOrder = false)
compareHkLambda(tp2, tp1, inOrder = false) ||
compareAliasedRefined(tp2, tp1, inOrder = false)
}
else // fast path, in particular for refinements resulting from parameterization.
isSubType(tp1, skipped2) &&
Expand Down Expand Up @@ -491,7 +492,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
}
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
case tp1: RefinedType =>
isNewSubType(tp1.parent, tp2) || compareHkLambda(tp1, tp2, inOrder = true)
isNewSubType(tp1.parent, tp2) ||
compareHkLambda(tp1, tp2, inOrder = true) ||
compareAliasedRefined(tp1, tp2, inOrder = true)
case AndType(tp11, tp12) =>
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
// and analogously for T11 & (T121 | T122) & T12 <: T2
Expand Down Expand Up @@ -614,6 +617,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
false
}

/** Say we are comparing a refined type `P{type M = U}` or `P{type M >: L <: U}`.
* If P#M refers to a BaseTypeArg aliased to some other typeref P#N,
* do the same comparison with `P{type N = U}` or `P{type N >: L <: U}`, respectively.
* This allows to handle situations involving named type params like this one:
*
* trait Lambda[type Elem]
* trait Lst[T] extends Lambda[T]
*
* compareAliasedRefined is necessary so we establish that
*
* Lst[Int] = Lst[Elem = Int]
*/
private def compareAliasedRefined(rt: RefinedType, other: Type, inOrder: Boolean) = {
val mbr = refinedSymbol(rt)
mbr.is(BaseTypeArg) && {
mbr.info match {
case TypeAlias(TypeRef(_, aliasName)) =>
val rt1 = rt.derivedRefinedType(rt.parent, aliasName, rt.refinedInfo)
subtyping.println(i"rewiring $rt to $rt1 in comparison with $other")
if (inOrder) isSubType(rt1, other) else isSubType(other, rt1)
case _ =>
false
}
}
}

/** The symbol referred to in the refinement of `rt` */
private def refinedSymbol(rt: RefinedType) = rt.parent.member(rt.refinedName).symbol

/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
* to keep the constraint as wide as possible. Specifically, if
*
Expand Down Expand Up @@ -742,11 +774,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
/** A type has been covered previously in subtype checking if it
* is some combination of TypeRefs that point to classes, where the
* combiners are RefinedTypes, AndTypes or AnnotatedTypes.
* One exception: Refinements referring to basetype args are never considered
* to be already covered. This is necessary because such refined types might
* still need to be compared with a compareAliasRefined.
*/
private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match {
case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
case tp: ProtoType => false
case tp: RefinedType => isCovered(tp.parent)
case tp: RefinedType => isCovered(tp.parent) && !refinedSymbol(tp).is(BaseTypeArg)
case tp: AnnotatedType => isCovered(tp.underlying)
case AndType(tp1, tp2) => isCovered(tp1) && isCovered(tp2)
case _ => false
Expand Down
14 changes: 10 additions & 4 deletions src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,16 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
case tp: TypeRef =>
tp
case tp @ RefinedType(tp1, name: TypeName) =>
val prevInfo = refinements(name)
refinements = refinements.updated(name,
if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo)
formals = formals.updated(name, tp1.typeParamNamed(name))
tp.refinedInfo match {
case TypeAlias(TypeRef(pre, name1)) if name1 == name && (pre =:= cls.thisType) =>
// Don't record refinements of the form X = this.X (These can arise using named parameters).
typr.println(s"dropping refinement $tp")
case _ =>
val prevInfo = refinements(name)
refinements = refinements.updated(name,
if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo)
formals = formals.updated(name, tp1.typeParamNamed(name))
}
normalizeToRef(tp1)
case ErrorType =>
defn.AnyType
Expand Down
16 changes: 10 additions & 6 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,13 @@ object Types {
}
def goThis(tp: ThisType) = {
val d = go(tp.underlying)
if (d.exists) d
if (d.exists)
if ((pre eq tp) && d.symbol.is(NamedTypeParam) && (d.symbol.owner eq tp.cls))
// If we look for a named type parameter `P` in `C.this.P`, looking up
// the fully applied self type of `C` will give as an info the alias type
// `P = this.P`. We need to return a denotation with the underlying bounds instead.
d.symbol.denot
else d
else
// There is a special case to handle:
// trait Super { this: Sub => private class Inner {} println(this.Inner) }
Expand Down Expand Up @@ -854,11 +860,9 @@ object Types {
else NoType
case tp: AnnotatedType => tp.underlying.underlyingClassRef(refinementOK)
case tp: RefinedType =>
if (refinementOK) tp.underlying.underlyingClassRef(refinementOK)
else {
val tycon = tp.withoutArgs(tp.argInfos)
if (tycon eq tp) NoType else tycon.underlyingClassRef(refinementOK)
}
def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName)
if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK)
else NoType
case _ => NoType
}

Expand Down
14 changes: 3 additions & 11 deletions src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,10 @@ object Scala2Unpickler {
} else {
registerCompanionPair(scalacCompanion, denot.classSymbol)
}
val declsTypeParams = denot.typeParams
val declsInRightOrder =
if (declsTypeParams.corresponds(tparams)(_.name == _.name)) decls
else { // create new scope with type parameters in right order
val decls1 = newScope
for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name))
for (sym <- decls) if (!declsTypeParams.contains(sym)) decls1.enter(sym)
decls1
}

denot.info = ClassInfo( // final info
denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost)
denot.info = ClassInfo( // final info, except possibly for typeparams ordering
denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost)
denot.updateTypeParams(tparams)
}
}

Expand Down
46 changes: 38 additions & 8 deletions src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ object Parsers {

private def simpleTypeRest(t: Tree): Tree = in.token match {
case HASH => simpleTypeRest(typeProjection(t))
case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs()) })
case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs(namedOK = true)) })
case _ => t
}

Expand All @@ -759,9 +759,38 @@ object Parsers {
}
else typ()

/** NamedTypeArg ::= id `=' ArgType
*/
val namedTypeArg = () => {
val name = ident()
accept(EQUALS)
NamedArg(name.toTypeName, argType())
}

/** ArgTypes ::= ArgType {`,' ArgType}
* NamedTypeArg {`,' NamedTypeArg}
*/
def argTypes() = commaSeparated(argType)
def argTypes(namedOK: Boolean = false) = {
def otherArgs(first: Tree, arg: () => Tree): List[Tree] = {
val rest =
if (in.token == COMMA) {
in.nextToken()
commaSeparated(arg)
}
else Nil
first :: rest
}
if (namedOK && in.token == IDENTIFIER)
argType() match {
case Ident(name) if in.token == EQUALS =>
in.nextToken()
otherArgs(NamedArg(name, argType()), namedTypeArg)
case firstArg =>
if (in.token == EQUALS) println(s"??? $firstArg")
otherArgs(firstArg, argType)
}
else commaSeparated(argType)
}

/** FunArgType ::= ArgType | `=>' ArgType
*/
Expand All @@ -785,9 +814,10 @@ object Parsers {
} else t
}

/** TypeArgs ::= `[' ArgType {`,' ArgType} `]'
*/
def typeArgs(): List[Tree] = inBrackets(argTypes())
/** TypeArgs ::= `[' ArgType {`,' ArgType} `]'
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
*/
def typeArgs(namedOK: Boolean = false): List[Tree] = inBrackets(argTypes(namedOK))

/** Refinement ::= `{' RefineStatSeq `}'
*/
Expand Down Expand Up @@ -1045,7 +1075,7 @@ object Parsers {
* | Path
* | `(' [ExprsInParens] `)'
* | SimpleExpr `.' Id
* | SimpleExpr TypeArgs
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
*/
def simpleExpr(): Tree = {
Expand Down Expand Up @@ -1094,7 +1124,7 @@ object Parsers {
in.nextToken()
simpleExprRest(selector(t), canApply = true)
case LBRACKET =>
val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs()) }
val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) }
simpleExprRest(tapp, canApply = true)
case LPAREN | LBRACE if canApply =>
val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) }
Expand Down Expand Up @@ -1493,7 +1523,7 @@ object Parsers {
atPos(modStart, in.offset) {
if (in.token == TYPE) {
in.nextToken()
mods | Param
mods | Param | ParamAccessor
} else {
if (mods.hasFlags) syntaxError("`type' expected")
mods | Param | PrivateLocal
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor

// Drop accessors that are not retained from class scope
if (dropped.nonEmpty) {
val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
val clsInfo = cls.classInfo
cls.copy(
info = clsInfo.derivedClassInfo(
decls = clsInfo.decls.filteredScope(!dropped.contains(_))))
Expand Down
Loading