Skip to content

Fix/systematic prototypes #136

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 14 commits into from
Jun 20, 2014
Merged
10 changes: 9 additions & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ object desugar {
val constr = cpy.DefDef(constr1,
constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs)

// Add constructor type parameters to auxiliary constructors
val normalizedBody = body map {
case ddef: DefDef if ddef.name.isConstructorName =>
cpy.DefDef(ddef, ddef.mods, ddef.name, constrTparams, ddef.vparamss, ddef.tpt, ddef.rhs)
case stat =>
stat
}

val derivedTparams = constrTparams map derivedTypeParam
val derivedVparamss = constrVparamss nestedMap derivedTermParam
val arity = constrVparamss.head.length
Expand Down Expand Up @@ -376,7 +384,7 @@ object desugar {
}
cpy.TypeDef(cdef, mods, name,
cpy.Template(impl, constr, parents1, self1,
tparamAccessors ::: vparamAccessors ::: body ::: caseClassMeths))
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths))
}

// install the watch on classTycon
Expand Down
51 changes: 51 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ object SymDenotations {
final def isAnonymousClass(implicit ctx: Context): Boolean =
initial.asSymDenotation.name startsWith tpnme.ANON_CLASS

final def isRefinementClass(implicit ctx: Context): Boolean =
name.decode == tpnme.REFINE_CLASS

/** Is this symbol a package object or its module class? */
def isPackageObject(implicit ctx: Context): Boolean = {
val poName = if (isType) nme.PACKAGE_CLS else nme.PACKAGE
Expand Down Expand Up @@ -701,6 +704,54 @@ object SymDenotations {
else
Iterator.empty

/** The symbol overriding this symbol in given subclass `ofclazz`.
*
* @param ofclazz is a subclass of this symbol's owner
*/
final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =
if (canMatchInheritedSymbols) matchingSymbol(inClass, inClass.thisType)
else NoSymbol

/** If false, this symbol cannot possibly participate in an override,
* either as overrider or overridee. For internal use; you should consult
* with isOverridingSymbol. This is used by isOverridingSymbol to escape
* the recursive knot.
*/
private def canMatchInheritedSymbols = (
owner.isClass
&& !this.isClass
&& !this.isConstructor
)

/** The symbol accessed by a super in the definition of this symbol when
* seen from class `base`. This symbol is always concrete.
* pre: `this.owner` is in the base class sequence of `base`.
*/
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
case bc :: bcs1 =>
val sym = matchingSymbol(bcs.head, base.thisType)
.suchThat(alt => !(alt is Deferred)).symbol
if (sym.exists) sym else loop(bcs.tail)
case _ =>
NoSymbol
}
loop(base.info.baseClasses.dropWhile(owner != _).tail)
}


/** A a member of class `base` is incomplete if
* (1) it is declared deferred or
* (2) it is abstract override and its super symbol in `base` is
* nonexistent or incomplete.
*/
final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
(this is Deferred) ||
(this is AbsOverride) && {
val supersym = superSymbolIn(base)
supersym == NoSymbol || supersym.isIncompleteIn(base)
}

/** The class or term symbol up to which this symbol is accessible,
* or RootClass if it is public. As java protected statics are
* otherwise completely inaccessible in scala, they are treated
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ class TypeComparer(initctx: Context) extends DotClass {
}

/** The greatest lower bound of two types */
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ {
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
if (tp1 eq tp2) tp1
else if (!tp1.exists) tp2
else if (!tp2.exists) tp1
Expand Down Expand Up @@ -892,7 +892,7 @@ class TypeComparer(initctx: Context) extends DotClass {
/** The least upper bound of two types
* @note We do not admit singleton types in or-types as lubs.
*/
def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ {
def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
if (tp1 eq tp2) tp1
else if (!tp1.exists) tp1
else if (!tp2.exists) tp2
Expand Down
13 changes: 7 additions & 6 deletions src/dotty/tools/dotc/core/TyperState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,13 @@ extends TyperState(r) {
val savedConstraint = myConstraint
myReporter = new StoreReporter
val result = op
if (!reporter.hasErrors) result
else {
myReporter = savedReporter
myConstraint = savedConstraint
fallback
}
try
if (!reporter.hasErrors) result
else {
myConstraint = savedConstraint
fallback
}
finally myReporter = savedReporter
}

override def toText(printer: Printer): Text = constraint.toText(printer)
Expand Down
11 changes: 6 additions & 5 deletions src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,6 @@ object Parsers {
if (tok == BACKQUOTED_IDENT) BackquotedIdent(name)
else Ident(name)

/** IdentOrWildcard ::= id | `_' */
def identOrWildcard(): Name =
if (in.token == USCORE) { in.nextToken(); nme.WILDCARD } else ident()

def wildcardIdent(): Ident =
atPos(accept(USCORE)) { Ident(nme.WILDCARD) }

Expand Down Expand Up @@ -1478,7 +1474,12 @@ object Parsers {
if (mods is VarianceFlags) in.nextToken()
}
atPos(tokenRange) {
val name = (if (isConcreteOwner) ident() else identOrWildcard()).toTypeName
val name =
if (isConcreteOwner || in.token != USCORE) ident().toTypeName
else {
in.nextToken()
ctx.freshName(nme.USCORE_PARAM_PREFIX).toTypeName
}
val hkparams =
if (ownerKind == ParamOwner.TypeParam) Nil
else typeParamClauseOpt(ParamOwner.TypeParam)
Expand Down
11 changes: 8 additions & 3 deletions src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package printing

import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._
import Contexts.Context, Scopes.Scope, Denotations._, Annotations.Annotation
import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation
import StdNames.nme
import ast.{Trees, untpd}
import typer.Namer
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto}
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType}
import Trees._
import scala.annotation.switch

Expand Down Expand Up @@ -126,7 +126,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tp: ViewProto =>
return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
case FunProto(args, resultType, _) =>
return "funproto(" ~ toTextGlobal(args, ", ") ~ "):" ~ toText(resultType)
val argsText = args match {
case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp)
case _ => toTextGlobal(args, ", ")
}
return "FunProto(" ~ argsText ~ "):" ~ toText(resultType)
case tp: IgnoredProto =>
return "?"
case _ =>
Expand Down Expand Up @@ -477,6 +481,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {

override def toText(denot: Denotation): Text = denot match {
case denot: MultiDenotation => denot.toString
case NoDenotation => "NoDenotation"
case _ =>
if (denot.symbol.exists) toText(denot.symbol)
else "some " ~ toText(denot.info)
Expand Down
15 changes: 11 additions & 4 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,16 @@ trait Applications extends Compatibility { self: Typer =>
protected def init() = methType match {
case methType: MethodType =>
// apply the result type constraint, unless method type is dependent
if (!methType.isDependent)
if (!methType.isDependent) {
val savedConstraint = ctx.typerState.constraint
if (!constrainResult(methType.resultType, resultType))
fail(err.typeMismatchStr(methType.resultType, resultType))
if (ctx.typerState.isCommittable)
// defer the problem until after the application;
// it might be healed by an implicit conversion
assert(ctx.typerState.constraint eq savedConstraint)
else
fail(err.typeMismatchStr(methType.resultType, resultType))
}
// match all arguments with corresponding formal parameters
matchArgs(orderedArgs, methType.paramTypes, 0)
case _ =>
Expand Down Expand Up @@ -434,7 +441,7 @@ trait Applications extends Compatibility { self: Typer =>
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {

def realApply(implicit ctx: Context): Tree = track("realApply") {
var proto = new FunProto(tree.args, ignoreIfProto(pt), this)
var proto = new FunProto(tree.args, IgnoredProto(pt), this)
val fun1 = typedExpr(tree.fun, proto)

// Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as
Expand All @@ -454,7 +461,7 @@ trait Applications extends Compatibility { self: Typer =>
val result = app.result
ConstFold(result)
} { (failedVal, failedState) =>
val fun2 = tryInsertImplicit(fun1, proto)
val fun2 = tryInsertImplicitOnQualifier(fun1, proto)
if (fun1 eq fun2) {
failedState.commit()
failedVal
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ trait Implicits { self: Typer =>

lazy val funProto = fullProto match {
case proto: ViewProto =>
FunProto(dummyTreeOfType(proto.argType) :: Nil, proto.resultType, self)
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
case proto => proto
}

Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ class Namer { typer: Typer =>

def checkedParentType(parent: untpd.Tree): Type = {
val ptype = parentType(parent)(ctx.fresh addMode Mode.InSuperCall)
checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head)
if (cls.isRefinementClass) ptype
else checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head)
}

val selfInfo =
Expand Down
19 changes: 10 additions & 9 deletions src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,8 @@ object ProtoTypes {
}

/** A class marking ignored prototypes that can be reviealed by `deepenProto` */
case class IgnoredProto(proto: ProtoType) extends UncachedGroundType with MatchAlways {
override def deepenProto(implicit ctx: Context): Type = proto
}

def ignoreIfProto(tp: Type): Type = tp match {
case proto: ProtoType => IgnoredProto(proto)
case _ => tp
case class IgnoredProto(ignored: Type) extends UncachedGroundType with MatchAlways {
override def deepenProto(implicit ctx: Context): Type = ignored
}

/** A prototype for expressions [] that are part of a selection operation:
Expand Down Expand Up @@ -145,7 +140,7 @@ object ProtoTypes {
if (name.isConstructorName) WildcardType
else tp match {
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
case tp => SelectionProto(name, ignoreIfProto(tp), typer)
case tp => SelectionProto(name, IgnoredProto(tp), typer)
}

/** A prototype for expressions [] that are in some unspecified selection operation
Expand Down Expand Up @@ -413,5 +408,11 @@ object ProtoTypes {
private lazy val dummyTree = untpd.Literal(Constant(null))

/** Dummy tree to be used as an argument of a FunProto or ViewProto type */
def dummyTreeOfType(tp: Type): Tree = dummyTree withTypeUnchecked tp
object dummyTreeOfType {
def apply(tp: Type): Tree = dummyTree withTypeUnchecked tp
def unapply(tree: Tree): Option[Type] = tree match {
case Literal(Constant(null)) => Some(tree.typeOpt)
case _ => None
}
}
}
51 changes: 29 additions & 22 deletions src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ object Typer {
val nothingBound = 0
def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport
}

/** Assert tree has a position, unless it is empty or a typed splice */
def assertPositioned(tree: untpd.Tree)(implicit ctx: Context) =
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable)
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
}

class Typer extends Namer with TypeAssigner with Applications with Implicits with Inferencing with Checking {
Expand Down Expand Up @@ -323,7 +328,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val tpt1 = typedType(tree.tpt)
val expr1 =
if (isWildcard) tree.expr withType tpt1.tpe
else typedExpr(tree.expr, tpt1.tpe)
else typed(tree.expr, tpt1.tpe)
assignType(cpy.Typed(tree, expr1, tpt1), tpt1)
}
tree.expr match {
Expand Down Expand Up @@ -953,7 +958,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}

def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {
if (!tree.isEmpty && ctx.typerState.isGlobalCommittable) assert(tree.pos.exists, i"position not set for $tree")
assertPositioned(tree)
try adapt(typedUnadapted(tree, pt), pt, tree)
catch {
case ex: CyclicReference => errorTree(tree, cyclicErrorMsg(ex))
Expand Down Expand Up @@ -1009,17 +1014,21 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}

/** Try to insert `.apply` so that the result conforms to prototype `pt`.
* If that fails try to insert an implicit conversion around the qualifier
* part of `tree`. If either result conforms to `pt`, adapt it, else
* continue with `fallBack`.
/** Add apply node or implicit conversions. Two strategies are tried, and the first
* that is succesful is picked. If neither of the strategies are succesful, continues with
* `fallBack`.
*
* 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
* 2nd stratgey: If tree is a select `qual.name`, try to insert an implicit conversion
* around the qualifier part `qual` so that the result conforms to the expected type
* with wildcard result type.
*/
def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
tryEither { implicit ctx =>
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
if (sel.tpe.isError) sel else adapt(sel, pt)
} { (failedTree, failedState) =>
val tree1 = tryInsertImplicit(tree, pt)
val tree1 = tryInsertImplicitOnQualifier(tree, pt)
if (tree1 eq tree) fallBack(failedTree, failedState)
else adapt(tree1, pt)
}
Expand All @@ -1028,21 +1037,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
* `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails
* return `tree` itself.
*/
def tryInsertImplicit(tree: Tree, pt: ProtoType)(implicit ctx: Context): Tree = ctx.traceIndented(i"try ins impl $tree $pt") { tree match {
case Select(qual, name) =>
val normalizedProto = pt match {
case pt: FunProto => pt.derivedFunProto(pt.args, WildcardType, pt.typer) // drop result type, because views are disabled
case _ => pt
}
val qualProto = SelectionProto(name, normalizedProto, NoViewsAllowed)
tryEither { implicit ctx =>
val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
if ((qual eq qual1) || ctx.reporter.hasErrors) tree
else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt)
} { (_, _) => tree
}
case _ => tree
}}
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
tree match {
case Select(qual, name) =>
val qualProto = SelectionProto(name, pt, NoViewsAllowed)
tryEither { implicit ctx =>
val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
if ((qual eq qual1) || ctx.reporter.hasErrors) tree
else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt)
} { (_, _) => tree
}
case _ => tree
}
}

def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = /*>|>*/ track("adapt") /*<|<*/ {
/*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {
Expand Down
1 change: 1 addition & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class tests extends CompilerTest {
@Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2)
@Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2)
@Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1)
@Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 13)

@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)
Expand Down
8 changes: 8 additions & 0 deletions tests/untried/pos/t2994a.scala → tests/neg/t2994.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ object Naturals {
}

}

object Test {
trait Bar[X[_]]
trait Baz[S[_] <: Bar[S]] {
type Apply[T]
}
trait Foo[V[_] <: Bar[V]] extends Bar[Baz[V]#Apply]
}
Loading