Skip to content

Commit 404ce76

Browse files
authored
Merge pull request scala#1826 from dotty-staging/fix-compile-stdlib
Make more parts of stdlib compile
2 parents ee4f4a1 + 9568eba commit 404ce76

File tree

16 files changed

+235
-243
lines changed

16 files changed

+235
-243
lines changed

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, F
77
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
88
import Decorators._
99
import language.higherKinds
10+
import typer.FrontEnd
1011
import collection.mutable.ListBuffer
1112
import util.Property
1213
import reporting.diagnostic.messages._
@@ -363,7 +364,7 @@ object desugar {
363364
if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
364365
else {
365366
def copyDefault(vparam: ValDef) =
366-
makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam))
367+
makeAnnotated("scala.annotation.unchecked.uncheckedVariance", refOfDef(vparam))
367368
val copyFirstParams = derivedVparamss.head.map(vparam =>
368369
cpy.ValDef(vparam)(rhs = copyDefault(vparam)))
369370
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
@@ -559,7 +560,7 @@ object desugar {
559560
case VarPattern(named, tpt) =>
560561
derivedValDef(original, named, tpt, rhs, mods)
561562
case _ =>
562-
val rhsUnchecked = makeAnnotated(defn.UncheckedAnnot, rhs)
563+
val rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
563564
val vars = getVariables(pat)
564565
val isMatchingTuple: Tree => Boolean = {
565566
case Tuple(es) => es.length == vars.length
@@ -688,11 +689,28 @@ object desugar {
688689
new ImplicitFunction(params, body)
689690
}
690691

691-
/** Add annotation with class `cls` to tree:
692-
* tree @cls
692+
/** Add annotation to tree:
693+
* tree @fullName
694+
*
695+
* The annotation is usually represented as a TypeTree referring to the class
696+
* with the given name `fullName`. However, if the annotation matches a file name
697+
* that is still to be entered, the annotation is represented as a cascade of `Selects`
698+
* following `fullName`. This is necessary so that we avoid reading an annotation from
699+
* the classpath that is also compiled from source.
693700
*/
694-
def makeAnnotated(cls: Symbol, tree: Tree)(implicit ctx: Context) =
695-
Annotated(tree, untpd.New(untpd.TypeTree(cls.typeRef), Nil))
701+
def makeAnnotated(fullName: String, tree: Tree)(implicit ctx: Context) = {
702+
val parts = fullName.split('.')
703+
val ttree = ctx.typerPhase match {
704+
case phase: FrontEnd if phase.stillToBeEntered(parts.last) =>
705+
val prefix =
706+
((Ident(nme.ROOTPKG): Tree) /: parts.init)((qual, name) =>
707+
Select(qual, name.toTermName))
708+
Select(prefix, parts.last.toTypeName)
709+
case _ =>
710+
TypeTree(ctx.requiredClass(fullName).typeRef)
711+
}
712+
Annotated(tree, untpd.New(ttree, Nil))
713+
}
696714

697715
private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit ctx: Context) = {
698716
val vdef = ValDef(named.name.asTermName, tpt, rhs)

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,8 +757,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
757757
if (args1.isEmpty) args2.isEmpty
758758
else args2.nonEmpty && {
759759
val v = tparams.head.paramVariance
760-
(v > 0 || isSubType(args2.head, args1.head)) &&
761-
(v < 0 || isSubType(args1.head, args2.head))
760+
def isSub(tp1: Type, tp2: Type) = tp2 match {
761+
case tp2: TypeBounds =>
762+
tp2.contains(tp1)
763+
case _ =>
764+
(v > 0 || isSubType(tp2, tp1)) &&
765+
(v < 0 || isSubType(tp1, tp2))
766+
}
767+
isSub(args1.head, args2.head)
762768
} && isSubArgs(args1.tail, args2.tail, tparams)
763769

764770
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
552552
def dynamicsEnabled =
553553
featureEnabled(defn.LanguageModuleClass, nme.dynamics)
554554

555-
def testScala2Mode(msg: String, pos: Position) = {
555+
def testScala2Mode(msg: => String, pos: Position) = {
556556
if (scala2Mode) migrationWarning(msg, pos)
557557
scala2Mode
558558
}

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ object Types {
217217
case _ => false
218218
}
219219

220+
/** Is this the type of a method with a leading empty parameter list?
221+
*/
222+
def isNullaryMethod(implicit ctx: Context): Boolean = this match {
223+
case MethodType(Nil, _) => true
224+
case tp: PolyType => tp.resultType.isNullaryMethod
225+
case _ => false
226+
}
227+
220228
/** Is this an alias TypeBounds? */
221229
def isAlias: Boolean = this.isInstanceOf[TypeAlias]
222230

compiler/src/dotty/tools/dotc/transform/ElimByName.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
7171

7272
def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
7373
case formalExpr: ExprType =>
74-
val argType = arg.tpe.widen
74+
val argType = arg.tpe.widenIfUnstable
7575
val argFun = arg match {
7676
case Apply(Select(qual, nme.apply), Nil)
7777
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>

compiler/src/dotty/tools/dotc/transform/LiftTry.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class LiftTry extends MiniPhase with IdentityDenotTransformer { thisTransform =>
5757
ctx.debuglog(i"lifting tree at ${tree.pos}, current owner = ${ctx.owner}")
5858
val fn = ctx.newSymbol(
5959
ctx.owner, ctx.freshName("liftedTree").toTermName, Synthetic | Method,
60-
MethodType(Nil, tree.tpe), coord = tree.pos)
60+
MethodType(Nil, tree.tpe.widenIfUnstable), coord = tree.pos)
6161
tree.changeOwnerAfter(ctx.owner, fn, thisTransform)
6262
Block(DefDef(fn, tree) :: Nil, ref(fn).appliedToNone)
6363
}

compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,24 @@ class SuperAccessors(thisTransformer: DenotTransformer) {
7171
val Select(qual, name) = sel
7272
val sym = sel.symbol
7373
val clazz = qual.symbol.asClass
74-
var supername = name.superName
75-
if (clazz is Trait) supername = supername.expandedName(clazz)
76-
77-
val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse {
78-
ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz")
79-
val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private
80-
val acc = ctx.newSymbol(
81-
clazz, supername, SuperAccessor | Artifact | Method | deferredOrPrivate,
82-
sel.tpe.widenSingleton.ensureMethodic, coord = sym.coord).enteredAfter(thisTransformer)
83-
// Diagnostic for SI-7091
84-
if (!accDefs.contains(clazz))
85-
ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos)
86-
else accDefs(clazz) += DefDef(acc, EmptyTree)
87-
acc
88-
}
74+
var superName = name.superName
75+
if (clazz is Trait) superName = superName.expandedName(clazz)
76+
val superInfo = sel.tpe.widenSingleton.ensureMethodic
77+
78+
val superAcc = clazz.info.decl(superName)
79+
.suchThat(_.signature == superInfo.signature).symbol
80+
.orElse {
81+
ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz")
82+
val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private
83+
val acc = ctx.newSymbol(
84+
clazz, superName, SuperAccessor | Artifact | Method | deferredOrPrivate,
85+
superInfo, coord = sym.coord).enteredAfter(thisTransformer)
86+
// Diagnostic for SI-7091
87+
if (!accDefs.contains(clazz))
88+
ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos)
89+
else accDefs(clazz) += DefDef(acc, EmptyTree)
90+
acc
91+
}
8992

9093
This(clazz).select(superAcc).withPos(sel.pos)
9194
}

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -657,18 +657,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
657657
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err)
658658
case TryDynamicCallType => typedDynamicApply(tree, pt)
659659
case _ =>
660-
tryEither {
661-
implicit ctx => simpleApply(fun1, proto)
662-
} {
663-
(failedVal, failedState) =>
664-
def fail = { failedState.commit(); failedVal }
665-
// Try once with original prototype and once (if different) with tupled one.
666-
// The reason we need to try both is that the decision whether to use tupled
667-
// or not was already taken but might have to be revised when an implicit
668-
// is inserted on the qualifier.
669-
tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
670-
if (proto eq originalProto) fail
671-
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
660+
if (originalProto.isDropped) fun1
661+
else
662+
tryEither {
663+
implicit ctx => simpleApply(fun1, proto)
664+
} {
665+
(failedVal, failedState) =>
666+
def fail = { failedState.commit(); failedVal }
667+
// Try once with original prototype and once (if different) with tupled one.
668+
// The reason we need to try both is that the decision whether to use tupled
669+
// or not was already taken but might have to be revised when an implicit
670+
// is inserted on the qualifier.
671+
tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
672+
if (proto eq originalProto) fail
673+
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
672674
}
673675
}
674676
}

compiler/src/dotty/tools/dotc/typer/FrontEnd.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ class FrontEnd extends Phase {
1919
override def isTyper = true
2020
import ast.tpd
2121

22+
/** The contexts for compilation units that are parsed but not yet entered */
23+
private var remaining: List[Context] = Nil
24+
25+
/** Does a source file ending with `<name>.scala` belong to a compilation unit
26+
* that is parsed but not yet entered?
27+
*/
28+
def stillToBeEntered(name: String): Boolean =
29+
remaining.exists(_.compilationUnit.toString.endsWith(name + ".scala"))
30+
2231
def monitor(doing: String)(body: => Unit)(implicit ctx: Context) =
2332
try body
2433
catch {
@@ -75,7 +84,11 @@ class FrontEnd extends Phase {
7584
}
7685
unitContexts foreach (parse(_))
7786
record("parsedTrees", ast.Trees.ntrees)
78-
unitContexts.foreach(enterSyms(_))
87+
remaining = unitContexts
88+
while (remaining.nonEmpty) {
89+
enterSyms(remaining.head)
90+
remaining = remaining.tail
91+
}
7992
unitContexts.foreach(enterAnnotations(_))
8093
unitContexts.foreach(typeCheck(_))
8194
record("total trees after typer", ast.Trees.ntrees)

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ object ProtoTypes {
4040
/** Test compatibility after normalization in a fresh typerstate. */
4141
def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = {
4242
val nestedCtx = ctx.fresh.setExploreTyperState
43-
isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
43+
val normTp = normalize(tp, pt)(nestedCtx)
44+
isCompatible(normTp, pt)(nestedCtx) ||
45+
pt.isRef(defn.UnitClass) && normTp.isParameterless
4446
}
4547

4648
private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match {
@@ -250,6 +252,22 @@ object ProtoTypes {
250252
/** Somebody called the `tupled` method of this prototype */
251253
def isTupled: Boolean = myTupled.isInstanceOf[FunProto]
252254

255+
/** If true, the application of this prototype was canceled. */
256+
private var toDrop: Boolean = false
257+
258+
/** Cancel the application of this prototype. This can happen for a nullary
259+
* application `f()` if `f` refers to a symbol that exists both in parameterless
260+
* form `def f` and nullary method form `def f()`. A common example for such
261+
* a method is `toString`. If in that case the type in the denotation is
262+
* parameterless, we compensate by dropping the application.
263+
*/
264+
def markAsDropped() = {
265+
assert(args.isEmpty)
266+
toDrop = true
267+
}
268+
269+
def isDropped: Boolean = toDrop
270+
253271
override def toString = s"FunProto(${args mkString ","} => $resultType)"
254272

255273
def map(tm: TypeMap)(implicit ctx: Context): FunProto =

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,9 @@ object RefChecks {
299299
!member.isAnyOverride) {
300300
// (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to
301301
// the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
302-
if (autoOverride(member))
302+
// Also excluded under Scala2 mode are overrides of default methods of Java traits.
303+
if (autoOverride(member) ||
304+
other.owner.is(JavaTrait) && ctx.testScala2Mode("`override' modifier required when a Java 8 default method is re-implemented", member.pos))
303305
member.setFlag(Override)
304306
else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
305307
emitOverrideError(
@@ -326,7 +328,8 @@ object RefChecks {
326328
overrideError("needs to be a stable, immutable value")
327329
} else if (member.is(ModuleVal) && !other.isRealMethod && !other.is(Deferred | Lazy)) {
328330
overrideError("may not override a concrete non-lazy value")
329-
} else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy)) {
331+
} else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy) &&
332+
!ctx.testScala2Mode("may not override a non-lazy value", member.pos)) {
330333
overrideError("may not override a non-lazy value")
331334
} else if (other.is(Lazy) && !other.isRealMethod && !member.is(Lazy)) {
332335
overrideError("must be declared lazy to override a lazy value")

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,13 +1640,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
16401640
case _ => false
16411641
}
16421642

1643-
/** Add apply node or implicit conversions. Two strategies are tried, and the first
1644-
* that is successful is picked. If neither of the strategies are successful, continues with
1645-
* `fallBack`.
1643+
/** Potentially add apply node or implicit conversions. Before trying either,
1644+
* if the function is applied to an empty parameter list (), we try
1645+
*
1646+
* 0th strategy: If `tree` overrides a nullary method, mark the prototype
1647+
* so that the argument is dropped and return `tree` itself.
1648+
*
1649+
* After that, two strategies are tried, and the first that is successful is picked.
1650+
* If neither of the strategies are successful, continues with`fallBack`.
16461651
*
16471652
* 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
16481653
* This strategy is not tried if the prototype represents already
16491654
* another `.apply` or `.apply()` selection.
1655+
*
16501656
* 2nd strategy: If tree is a select `qual.name`, try to insert an implicit conversion
16511657
* around the qualifier part `qual` so that the result conforms to the expected type
16521658
* with wildcard result type.
@@ -1661,8 +1667,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
16611667
def tryImplicit =
16621668
tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack)
16631669

1664-
if (isApplyProto(pt)) tryImplicit
1665-
else tryEither(tryApply(_))((_, _) => tryImplicit)
1670+
pt match {
1671+
case pt @ FunProto(Nil, _, _)
1672+
if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) =>
1673+
pt.markAsDropped()
1674+
tree
1675+
case _ =>
1676+
if (isApplyProto(pt)) tryImplicit
1677+
else tryEither(tryApply(_))((_, _) => tryImplicit)
1678+
}
16661679
}
16671680

16681681
/** If this tree is a select node `qual.name`, try to insert an implicit conversion

0 commit comments

Comments
 (0)