Skip to content

Commit a8cb929

Browse files
committed
Merge pull request #136 from dotty-staging/fix/systematic-prototypes
Fix/systematic prototypes
2 parents 7bc5cd1 + c2175ec commit a8cb929

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+502
-69
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ object desugar {
241241
val constr = cpy.DefDef(constr1,
242242
constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs)
243243

244+
// Add constructor type parameters to auxiliary constructors
245+
val normalizedBody = body map {
246+
case ddef: DefDef if ddef.name.isConstructorName =>
247+
cpy.DefDef(ddef, ddef.mods, ddef.name, constrTparams, ddef.vparamss, ddef.tpt, ddef.rhs)
248+
case stat =>
249+
stat
250+
}
251+
244252
val derivedTparams = constrTparams map derivedTypeParam
245253
val derivedVparamss = constrVparamss nestedMap derivedTermParam
246254
val arity = constrVparamss.head.length
@@ -376,7 +384,7 @@ object desugar {
376384
}
377385
cpy.TypeDef(cdef, mods, name,
378386
cpy.Template(impl, constr, parents1, self1,
379-
tparamAccessors ::: vparamAccessors ::: body ::: caseClassMeths))
387+
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths))
380388
}
381389

382390
// install the watch on classTycon

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ object SymDenotations {
321321
final def isAnonymousClass(implicit ctx: Context): Boolean =
322322
initial.asSymDenotation.name startsWith tpnme.ANON_CLASS
323323

324+
final def isRefinementClass(implicit ctx: Context): Boolean =
325+
name.decode == tpnme.REFINE_CLASS
326+
324327
/** Is this symbol a package object or its module class? */
325328
def isPackageObject(implicit ctx: Context): Boolean = {
326329
val poName = if (isType) nme.PACKAGE_CLS else nme.PACKAGE
@@ -701,6 +704,54 @@ object SymDenotations {
701704
else
702705
Iterator.empty
703706

707+
/** The symbol overriding this symbol in given subclass `ofclazz`.
708+
*
709+
* @param ofclazz is a subclass of this symbol's owner
710+
*/
711+
final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =
712+
if (canMatchInheritedSymbols) matchingSymbol(inClass, inClass.thisType)
713+
else NoSymbol
714+
715+
/** If false, this symbol cannot possibly participate in an override,
716+
* either as overrider or overridee. For internal use; you should consult
717+
* with isOverridingSymbol. This is used by isOverridingSymbol to escape
718+
* the recursive knot.
719+
*/
720+
private def canMatchInheritedSymbols = (
721+
owner.isClass
722+
&& !this.isClass
723+
&& !this.isConstructor
724+
)
725+
726+
/** The symbol accessed by a super in the definition of this symbol when
727+
* seen from class `base`. This symbol is always concrete.
728+
* pre: `this.owner` is in the base class sequence of `base`.
729+
*/
730+
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
731+
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
732+
case bc :: bcs1 =>
733+
val sym = matchingSymbol(bcs.head, base.thisType)
734+
.suchThat(alt => !(alt is Deferred)).symbol
735+
if (sym.exists) sym else loop(bcs.tail)
736+
case _ =>
737+
NoSymbol
738+
}
739+
loop(base.info.baseClasses.dropWhile(owner != _).tail)
740+
}
741+
742+
743+
/** A a member of class `base` is incomplete if
744+
* (1) it is declared deferred or
745+
* (2) it is abstract override and its super symbol in `base` is
746+
* nonexistent or incomplete.
747+
*/
748+
final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
749+
(this is Deferred) ||
750+
(this is AbsOverride) && {
751+
val supersym = superSymbolIn(base)
752+
supersym == NoSymbol || supersym.isIncompleteIn(base)
753+
}
754+
704755
/** The class or term symbol up to which this symbol is accessible,
705756
* or RootClass if it is public. As java protected statics are
706757
* otherwise completely inaccessible in scala, they are treated

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ class TypeComparer(initctx: Context) extends DotClass {
860860
}
861861

862862
/** The greatest lower bound of two types */
863-
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ {
863+
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
864864
if (tp1 eq tp2) tp1
865865
else if (!tp1.exists) tp2
866866
else if (!tp2.exists) tp1
@@ -892,7 +892,7 @@ class TypeComparer(initctx: Context) extends DotClass {
892892
/** The least upper bound of two types
893893
* @note We do not admit singleton types in or-types as lubs.
894894
*/
895-
def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ {
895+
def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
896896
if (tp1 eq tp2) tp1
897897
else if (!tp1.exists) tp1
898898
else if (!tp2.exists) tp2

src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,13 @@ extends TyperState(r) {
152152
val savedConstraint = myConstraint
153153
myReporter = new StoreReporter
154154
val result = op
155-
if (!reporter.hasErrors) result
156-
else {
157-
myReporter = savedReporter
158-
myConstraint = savedConstraint
159-
fallback
160-
}
155+
try
156+
if (!reporter.hasErrors) result
157+
else {
158+
myConstraint = savedConstraint
159+
fallback
160+
}
161+
finally myReporter = savedReporter
161162
}
162163

163164
override def toText(printer: Printer): Text = constraint.toText(printer)

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,6 @@ object Parsers {
447447
if (tok == BACKQUOTED_IDENT) BackquotedIdent(name)
448448
else Ident(name)
449449

450-
/** IdentOrWildcard ::= id | `_' */
451-
def identOrWildcard(): Name =
452-
if (in.token == USCORE) { in.nextToken(); nme.WILDCARD } else ident()
453-
454450
def wildcardIdent(): Ident =
455451
atPos(accept(USCORE)) { Ident(nme.WILDCARD) }
456452

@@ -1478,7 +1474,12 @@ object Parsers {
14781474
if (mods is VarianceFlags) in.nextToken()
14791475
}
14801476
atPos(tokenRange) {
1481-
val name = (if (isConcreteOwner) ident() else identOrWildcard()).toTypeName
1477+
val name =
1478+
if (isConcreteOwner || in.token != USCORE) ident().toTypeName
1479+
else {
1480+
in.nextToken()
1481+
ctx.freshName(nme.USCORE_PARAM_PREFIX).toTypeName
1482+
}
14821483
val hkparams =
14831484
if (ownerKind == ParamOwner.TypeParam) Nil
14841485
else typeParamClauseOpt(ParamOwner.TypeParam)

src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package printing
33

44
import core._
55
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._
6-
import Contexts.Context, Scopes.Scope, Denotations._, Annotations.Annotation
6+
import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation
77
import StdNames.nme
88
import ast.{Trees, untpd}
99
import typer.Namer
10-
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto}
10+
import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType}
1111
import Trees._
1212
import scala.annotation.switch
1313

@@ -126,7 +126,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
126126
case tp: ViewProto =>
127127
return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
128128
case FunProto(args, resultType, _) =>
129-
return "funproto(" ~ toTextGlobal(args, ", ") ~ "):" ~ toText(resultType)
129+
val argsText = args match {
130+
case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp)
131+
case _ => toTextGlobal(args, ", ")
132+
}
133+
return "FunProto(" ~ argsText ~ "):" ~ toText(resultType)
130134
case tp: IgnoredProto =>
131135
return "?"
132136
case _ =>
@@ -477,6 +481,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
477481

478482
override def toText(denot: Denotation): Text = denot match {
479483
case denot: MultiDenotation => denot.toString
484+
case NoDenotation => "NoDenotation"
480485
case _ =>
481486
if (denot.symbol.exists) toText(denot.symbol)
482487
else "some " ~ toText(denot.info)

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,16 @@ trait Applications extends Compatibility { self: Typer =>
125125
protected def init() = methType match {
126126
case methType: MethodType =>
127127
// apply the result type constraint, unless method type is dependent
128-
if (!methType.isDependent)
128+
if (!methType.isDependent) {
129+
val savedConstraint = ctx.typerState.constraint
129130
if (!constrainResult(methType.resultType, resultType))
130-
fail(err.typeMismatchStr(methType.resultType, resultType))
131+
if (ctx.typerState.isCommittable)
132+
// defer the problem until after the application;
133+
// it might be healed by an implicit conversion
134+
assert(ctx.typerState.constraint eq savedConstraint)
135+
else
136+
fail(err.typeMismatchStr(methType.resultType, resultType))
137+
}
131138
// match all arguments with corresponding formal parameters
132139
matchArgs(orderedArgs, methType.paramTypes, 0)
133140
case _ =>
@@ -434,7 +441,7 @@ trait Applications extends Compatibility { self: Typer =>
434441
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
435442

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

440447
// Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as
@@ -454,7 +461,7 @@ trait Applications extends Compatibility { self: Typer =>
454461
val result = app.result
455462
ConstFold(result)
456463
} { (failedVal, failedState) =>
457-
val fun2 = tryInsertImplicit(fun1, proto)
464+
val fun2 = tryInsertImplicitOnQualifier(fun1, proto)
458465
if (fun1 eq fun2) {
459466
failedState.commit()
460467
failedVal

src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ trait Implicits { self: Typer =>
441441

442442
lazy val funProto = fullProto match {
443443
case proto: ViewProto =>
444-
FunProto(dummyTreeOfType(proto.argType) :: Nil, proto.resultType, self)
444+
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
445445
case proto => proto
446446
}
447447

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,8 @@ class Namer { typer: Typer =>
459459

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

465466
val selfInfo =

src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,8 @@ object ProtoTypes {
7979
}
8080

8181
/** A class marking ignored prototypes that can be reviealed by `deepenProto` */
82-
case class IgnoredProto(proto: ProtoType) extends UncachedGroundType with MatchAlways {
83-
override def deepenProto(implicit ctx: Context): Type = proto
84-
}
85-
86-
def ignoreIfProto(tp: Type): Type = tp match {
87-
case proto: ProtoType => IgnoredProto(proto)
88-
case _ => tp
82+
case class IgnoredProto(ignored: Type) extends UncachedGroundType with MatchAlways {
83+
override def deepenProto(implicit ctx: Context): Type = ignored
8984
}
9085

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

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

415410
/** Dummy tree to be used as an argument of a FunProto or ViewProto type */
416-
def dummyTreeOfType(tp: Type): Tree = dummyTree withTypeUnchecked tp
411+
object dummyTreeOfType {
412+
def apply(tp: Type): Tree = dummyTree withTypeUnchecked tp
413+
def unapply(tree: Tree): Option[Type] = tree match {
414+
case Literal(Constant(null)) => Some(tree.typeOpt)
415+
case _ => None
416+
}
417+
}
417418
}

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ object Typer {
4646
val nothingBound = 0
4747
def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport
4848
}
49+
50+
/** Assert tree has a position, unless it is empty or a typed splice */
51+
def assertPositioned(tree: untpd.Tree)(implicit ctx: Context) =
52+
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable)
53+
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
4954
}
5055

5156
class Typer extends Namer with TypeAssigner with Applications with Implicits with Inferencing with Checking {
@@ -323,7 +328,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
323328
val tpt1 = typedType(tree.tpt)
324329
val expr1 =
325330
if (isWildcard) tree.expr withType tpt1.tpe
326-
else typedExpr(tree.expr, tpt1.tpe)
331+
else typed(tree.expr, tpt1.tpe)
327332
assignType(cpy.Typed(tree, expr1, tpt1), tpt1)
328333
}
329334
tree.expr match {
@@ -953,7 +958,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
953958
}
954959

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

1012-
/** Try to insert `.apply` so that the result conforms to prototype `pt`.
1013-
* If that fails try to insert an implicit conversion around the qualifier
1014-
* part of `tree`. If either result conforms to `pt`, adapt it, else
1015-
* continue with `fallBack`.
1017+
/** Add apply node or implicit conversions. Two strategies are tried, and the first
1018+
* that is succesful is picked. If neither of the strategies are succesful, continues with
1019+
* `fallBack`.
1020+
*
1021+
* 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
1022+
* 2nd stratgey: If tree is a select `qual.name`, try to insert an implicit conversion
1023+
* around the qualifier part `qual` so that the result conforms to the expected type
1024+
* with wildcard result type.
10161025
*/
10171026
def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
10181027
tryEither { implicit ctx =>
10191028
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
10201029
if (sel.tpe.isError) sel else adapt(sel, pt)
10211030
} { (failedTree, failedState) =>
1022-
val tree1 = tryInsertImplicit(tree, pt)
1031+
val tree1 = tryInsertImplicitOnQualifier(tree, pt)
10231032
if (tree1 eq tree) fallBack(failedTree, failedState)
10241033
else adapt(tree1, pt)
10251034
}
@@ -1028,21 +1037,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
10281037
* `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails
10291038
* return `tree` itself.
10301039
*/
1031-
def tryInsertImplicit(tree: Tree, pt: ProtoType)(implicit ctx: Context): Tree = ctx.traceIndented(i"try ins impl $tree $pt") { tree match {
1032-
case Select(qual, name) =>
1033-
val normalizedProto = pt match {
1034-
case pt: FunProto => pt.derivedFunProto(pt.args, WildcardType, pt.typer) // drop result type, because views are disabled
1035-
case _ => pt
1036-
}
1037-
val qualProto = SelectionProto(name, normalizedProto, NoViewsAllowed)
1038-
tryEither { implicit ctx =>
1039-
val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
1040-
if ((qual eq qual1) || ctx.reporter.hasErrors) tree
1041-
else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt)
1042-
} { (_, _) => tree
1043-
}
1044-
case _ => tree
1045-
}}
1040+
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
1041+
tree match {
1042+
case Select(qual, name) =>
1043+
val qualProto = SelectionProto(name, pt, NoViewsAllowed)
1044+
tryEither { implicit ctx =>
1045+
val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
1046+
if ((qual eq qual1) || ctx.reporter.hasErrors) tree
1047+
else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt)
1048+
} { (_, _) => tree
1049+
}
1050+
case _ => tree
1051+
}
1052+
}
10461053

10471054
def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = /*>|>*/ track("adapt") /*<|<*/ {
10481055
/*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class tests extends CompilerTest {
8484
@Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2)
8585
@Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2)
8686
@Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1)
87+
@Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 13)
8788

8889
@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)
8990
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)

tests/untried/pos/t2994a.scala renamed to tests/neg/t2994.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,11 @@ object Naturals {
2525
}
2626

2727
}
28+
29+
object Test {
30+
trait Bar[X[_]]
31+
trait Baz[S[_] <: Bar[S]] {
32+
type Apply[T]
33+
}
34+
trait Foo[V[_] <: Bar[V]] extends Bar[Baz[V]#Apply]
35+
}

0 commit comments

Comments
 (0)