Skip to content

Make more parts of stdlib compile #1826

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 20 commits into from
Dec 21, 2016
Merged
Show file tree
Hide file tree
Changes from 19 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
30 changes: 24 additions & 6 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, F
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
import language.higherKinds
import typer.FrontEnd
import collection.mutable.ListBuffer
import util.Property
import reporting.diagnostic.messages._
Expand Down Expand Up @@ -363,7 +364,7 @@ object desugar {
if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
else {
def copyDefault(vparam: ValDef) =
makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam))
makeAnnotated("scala.annotation.unchecked.uncheckedVariance", refOfDef(vparam))
val copyFirstParams = derivedVparamss.head.map(vparam =>
cpy.ValDef(vparam)(rhs = copyDefault(vparam)))
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
Expand Down Expand Up @@ -559,7 +560,7 @@ object desugar {
case VarPattern(named, tpt) =>
derivedValDef(original, named, tpt, rhs, mods)
case _ =>
val rhsUnchecked = makeAnnotated(defn.UncheckedAnnot, rhs)
val rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
val vars = getVariables(pat)
val isMatchingTuple: Tree => Boolean = {
case Tuple(es) => es.length == vars.length
Expand Down Expand Up @@ -688,11 +689,28 @@ object desugar {
new ImplicitFunction(params, body)
}

/** Add annotation with class `cls` to tree:
* tree @cls
/** Add annotation to tree:
* tree @fullName
*
* The annotation is usually represented as a TypeTree referring to the class
* with the given name `fullName`. However, if the annotation matches a file name
* that is still to be entered, the annotation is represented as a cascade of `Selects`
* following `fullName`. This is necessary so that we avoid reading an annotation from
* the classpath that is also compiled from source.
*/
def makeAnnotated(cls: Symbol, tree: Tree)(implicit ctx: Context) =
Annotated(tree, untpd.New(untpd.TypeTree(cls.typeRef), Nil))
def makeAnnotated(fullName: String, tree: Tree)(implicit ctx: Context) = {
val parts = fullName.split('.')
val ttree = ctx.typerPhase match {
case phase: FrontEnd if phase.stillToBeEntered(parts.last) =>
val prefix =
((Ident(nme.ROOTPKG): Tree) /: parts.init)((qual, name) =>
Select(qual, name.toTermName))
Select(prefix, parts.last.toTypeName)
case _ =>
TypeTree(ctx.requiredClass(fullName).typeRef)
}
Annotated(tree, untpd.New(ttree, Nil))
}

private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit ctx: Context) = {
val vdef = ValDef(named.name.asTermName, tpt, rhs)
Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
if (args1.isEmpty) args2.isEmpty
else args2.nonEmpty && {
val v = tparams.head.paramVariance
(v > 0 || isSubType(args2.head, args1.head)) &&
(v < 0 || isSubType(args1.head, args2.head))
def isSub(tp1: Type, tp2: Type) = tp2 match {
case tp2: TypeBounds =>
tp2.contains(tp1)
case _ =>
(v > 0 || isSubType(tp2, tp1)) &&
(v < 0 || isSubType(tp1, tp2))
}
isSub(args1.head, args2.head)
} && isSubArgs(args1.tail, args2.tail, tparams)

/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
def dynamicsEnabled =
featureEnabled(defn.LanguageModuleClass, nme.dynamics)

def testScala2Mode(msg: String, pos: Position) = {
def testScala2Mode(msg: => String, pos: Position) = {
if (scala2Mode) migrationWarning(msg, pos)
scala2Mode
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ object Types {
case _ => false
}

/** Is this the type of a method with a leading empty parameter list?
*/
def isNullaryMethod(implicit ctx: Context): Boolean = this match {
case MethodType(Nil, _) => true
case tp: PolyType => tp.resultType.isNullaryMethod
case _ => false
}

/** Is this an alias TypeBounds? */
def isAlias: Boolean = this.isInstanceOf[TypeAlias]

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ElimByName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform

def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
case formalExpr: ExprType =>
val argType = arg.tpe.widen
val argType = arg.tpe.widenIfUnstable
val argFun = arg match {
case Apply(Select(qual, nme.apply), Nil)
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/LiftTry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class LiftTry extends MiniPhase with IdentityDenotTransformer { thisTransform =>
ctx.debuglog(i"lifting tree at ${tree.pos}, current owner = ${ctx.owner}")
val fn = ctx.newSymbol(
ctx.owner, ctx.freshName("liftedTree").toTermName, Synthetic | Method,
MethodType(Nil, tree.tpe), coord = tree.pos)
MethodType(Nil, tree.tpe.widenIfUnstable), coord = tree.pos)
tree.changeOwnerAfter(ctx.owner, fn, thisTransform)
Block(DefDef(fn, tree) :: Nil, ref(fn).appliedToNone)
}
Expand Down
33 changes: 18 additions & 15 deletions compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,24 @@ class SuperAccessors(thisTransformer: DenotTransformer) {
val Select(qual, name) = sel
val sym = sel.symbol
val clazz = qual.symbol.asClass
var supername = name.superName
if (clazz is Trait) supername = supername.expandedName(clazz)

val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse {
ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz")
val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private
val acc = ctx.newSymbol(
clazz, supername, SuperAccessor | Artifact | Method | deferredOrPrivate,
sel.tpe.widenSingleton.ensureMethodic, coord = sym.coord).enteredAfter(thisTransformer)
// Diagnostic for SI-7091
if (!accDefs.contains(clazz))
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)
else accDefs(clazz) += DefDef(acc, EmptyTree)
acc
}
var superName = name.superName
if (clazz is Trait) superName = superName.expandedName(clazz)
val superInfo = sel.tpe.widenSingleton.ensureMethodic

val superAcc = clazz.info.decl(superName)
.suchThat(_.signature == superInfo.signature).symbol
.orElse {
ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz")
val deferredOrPrivate = if (clazz is Trait) Deferred | ExpandedName else Private
val acc = ctx.newSymbol(
clazz, superName, SuperAccessor | Artifact | Method | deferredOrPrivate,
superInfo, coord = sym.coord).enteredAfter(thisTransformer)
// Diagnostic for SI-7091
if (!accDefs.contains(clazz))
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)
else accDefs(clazz) += DefDef(acc, EmptyTree)
acc
}

This(clazz).select(superAcc).withPos(sel.pos)
}
Expand Down
26 changes: 14 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -657,18 +657,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err)
case TryDynamicCallType => typedDynamicApply(tree, pt)
case _ =>
tryEither {
implicit ctx => simpleApply(fun1, proto)
} {
(failedVal, failedState) =>
def fail = { failedState.commit(); failedVal }
// Try once with original prototype and once (if different) with tupled one.
// The reason we need to try both is that the decision whether to use tupled
// or not was already taken but might have to be revised when an implicit
// is inserted on the qualifier.
tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
if (proto eq originalProto) fail
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
if (originalProto.isDropped) fun1
else
tryEither {
implicit ctx => simpleApply(fun1, proto)
} {
(failedVal, failedState) =>
def fail = { failedState.commit(); failedVal }
// Try once with original prototype and once (if different) with tupled one.
// The reason we need to try both is that the decision whether to use tupled
// or not was already taken but might have to be revised when an implicit
// is inserted on the qualifier.
tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
if (proto eq originalProto) fail
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ class FrontEnd extends Phase {
override def isTyper = true
import ast.tpd

/** The contexts for compilation units that are parsed but not yet entered */
private var remaining: List[Context] = Nil

/** Does a source file ending with `<name>.scala` belong to a compilation unit
* that is parsed but not yet entered?
*/
def stillToBeEntered(name: String): Boolean =
remaining.exists(_.compilationUnit.toString.endsWith(name + ".scala"))

def monitor(doing: String)(body: => Unit)(implicit ctx: Context) =
try body
catch {
Expand Down Expand Up @@ -75,7 +84,11 @@ class FrontEnd extends Phase {
}
unitContexts foreach (parse(_))
record("parsedTrees", ast.Trees.ntrees)
unitContexts.foreach(enterSyms(_))
remaining = unitContexts
while (remaining.nonEmpty) {
enterSyms(remaining.head)
remaining = remaining.tail
}
unitContexts.foreach(enterAnnotations(_))
unitContexts.foreach(typeCheck(_))
record("total trees after typer", ast.Trees.ntrees)
Expand Down
20 changes: 19 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ object ProtoTypes {
/** Test compatibility after normalization in a fresh typerstate. */
def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = {
val nestedCtx = ctx.fresh.setExploreTyperState
isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
val normTp = normalize(tp, pt)(nestedCtx)
isCompatible(normTp, pt)(nestedCtx) ||
pt.isRef(defn.UnitClass) && normTp.isParameterless
}

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

/** If true, the application of this prototype was canceled. */
private var toDrop: Boolean = false

/** Cancel the application of this prototype. This can happen for a nullary
* application `f()` if `f` refers to a symbol that exists both in parameterless
* form `def f` and nullary method form `def f()`. A common example for such
* a method is `toString`. If in that case the type in the denotation is
* parameterless, we compensate by dropping the application.
*/
def markAsDropped() = {
assert(args.isEmpty)
toDrop = true
}

def isDropped: Boolean = toDrop

override def toString = s"FunProto(${args mkString ","} => $resultType)"

def map(tm: TypeMap)(implicit ctx: Context): FunProto =
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ object RefChecks {
!member.isAnyOverride) {
// (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to
// the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
if (autoOverride(member))
// Also excluded under Scala2 mode are overrides of default methods of Java traits.
if (autoOverride(member) ||
other.owner.is(JavaTrait) && ctx.testScala2Mode("`override' modifier required when a Java 8 default method is re-implemented", member.pos))
member.setFlag(Override)
else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
emitOverrideError(
Expand All @@ -326,7 +328,8 @@ object RefChecks {
overrideError("needs to be a stable, immutable value")
} else if (member.is(ModuleVal) && !other.isRealMethod && !other.is(Deferred | Lazy)) {
overrideError("may not override a concrete non-lazy value")
} else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy)) {
} else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy) &&
!ctx.testScala2Mode("may not override a non-lazy value", member.pos)) {
overrideError("may not override a non-lazy value")
} else if (other.is(Lazy) && !other.isRealMethod && !member.is(Lazy)) {
overrideError("must be declared lazy to override a lazy value")
Expand Down
23 changes: 18 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1640,13 +1640,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ => false
}

/** Add apply node or implicit conversions. Two strategies are tried, and the first
* that is successful is picked. If neither of the strategies are successful, continues with
* `fallBack`.
/** Potentially add apply node or implicit conversions. Before trying either,
* if the function is applied to an empty parameter list (), we try
*
* 0th strategy: If `tree` overrides a nullary method, mark the prototype
* so that the argument is dropped and return `tree` itself.
*
* After that, two strategies are tried, and the firs that is successful is picked.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: firs -> first

* If neither of the strategies are successful, continues with`fallBack`.
*
* 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
* This strategy is not tried if the prototype represents already
* another `.apply` or `.apply()` selection.
*
* 2nd strategy: 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.
Expand All @@ -1661,8 +1667,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def tryImplicit =
tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack)

if (isApplyProto(pt)) tryImplicit
else tryEither(tryApply(_))((_, _) => tryImplicit)
pt match {
case pt @ FunProto(Nil, _, _)
if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) =>
pt.markAsDropped()
tree
case _ =>
if (isApplyProto(pt)) tryImplicit
else tryEither(tryApply(_))((_, _) => tryImplicit)
}
}

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