From 4f05aa9bf0d3c29eae720115433ffe29b239d70f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Oct 2013 14:55:00 +0200 Subject: Changes to adaptation and local type inference. --- src/dotty/tools/dotc/Compiler.scala | 2 +- src/dotty/tools/dotc/core/Contexts.scala | 4 ++-- src/dotty/tools/dotc/core/TyperState.scala | 11 +++++----- src/dotty/tools/dotc/typer/Implicits.scala | 31 ++++++++++++++-------------- src/dotty/tools/dotc/typer/Inferencing.scala | 11 ++++++++-- src/dotty/tools/dotc/typer/Namer.scala | 6 ++++-- src/dotty/tools/dotc/typer/Typer.scala | 28 ++++++++++++------------- 7 files changed, 52 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index e6675237a..9c71bdc96 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -23,7 +23,7 @@ class Compiler { .withOwner(defn.RootClass) .withTyper(new Typer) .withNewMode(Mode.ImplicitsEnabled) - .withTyperState(new MutableTyperState(ctx.typerState, new ConsoleReporter()(ctx), committable = true)) + .withTyperState(new MutableTyperState(ctx.typerState, new ConsoleReporter()(ctx), isCommittable = true)) def addImport(ctx: Context, sym: Symbol) = ctx.fresh.withImportInfo(ImportInfo.rootImport(sym)(ctx)) (start.withRunInfo(new RunInfo(start)) /: rootImports)(addImport) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 3de6337b1..d06670b94 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -288,8 +288,8 @@ object Contexts { def withPeriod(period: Period): this.type = { this.period = period; this } def withNewMode(mode: Mode): this.type = { this.mode = mode; this } def withTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this } - def withNewTyperState: this.type = withTyperState(typerState.fresh(committable = true)) - def withExploreTyperState: this.type = withTyperState(typerState.fresh(committable = false)) + def withNewTyperState: this.type = withTyperState(typerState.fresh(isCommittable = true)) + def withExploreTyperState: this.type = withTyperState(typerState.fresh(isCommittable = false)) def withPrinterFn(printer: Context => Printer): this.type = { this.printerFn = printer; this } def withOwner(owner: Symbol): this.type = { this.owner = owner; this } def withSettings(sstate: SettingsState): this.type = { this.sstate = sstate; this } diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index a4663f824..8fbd3502c 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -29,9 +29,10 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable { def undetVars_=(vs: Set[TypeVar]): Unit = unsupported("undetVars_=") def instType_=(m: SimpleMap[TypeVar, Type]): Unit = unsupported("instType_=") - def fresh(committable: Boolean): TyperState = this + def fresh(isCommittable: Boolean): TyperState = this def commit()(implicit ctx: Context): Unit = unsupported("commit") + def isCommittable: Boolean = false @elidable(elidable.FINER) def checkConsistent(implicit ctx: Context) = () @@ -44,7 +45,7 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable { override def toText(printer: Printer): Text = "ImmutableTyperState" } -class MutableTyperState(previous: TyperState, reporter: Reporter, committable: Boolean) +class MutableTyperState(previous: TyperState, reporter: Reporter, override val isCommittable: Boolean) extends TyperState(reporter) { private var myConstraint: Constraint = previous.constraint @@ -66,8 +67,8 @@ extends TyperState(reporter) { } override def instType_=(m: SimpleMap[TypeVar, Type]): Unit = myInstType = m - override def fresh(committable: Boolean): TyperState = - new MutableTyperState(this, new StoreReporter, committable) + override def fresh(isCommittable: Boolean): TyperState = + new MutableTyperState(this, new StoreReporter, isCommittable) /** Commit typer state so that its information is copied into current typer state * In addition (1) the owning state of undetermined or temporarily instantiated @@ -104,7 +105,7 @@ extends TyperState(reporter) { def err(msg: String, what: Showable) = s"$msg: ${show(what)}\n${show(this)}" for (tvar <- undetVars) assert(constraint(tvar.origin).exists, err("unconstrained type var", tvar.origin)) - if (committable) { + if (isCommittable) { val undetParams = undetVars map (_.origin) for (param <- constraint.domainParams) assert(undetParams contains param, err("junk constraint on", param)) diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 832693b98..05db467f0 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -27,6 +27,9 @@ import collection.mutable /** Implicit resolution */ object Implicits { + private def disableImplicits(ctx: Context): Context = + if (ctx == NoContext) ctx else ctx retractMode Mode.ImplicitsEnabled + /** A common base class of contextual implicits and of-type implicits which * represents as set of implicit references. */ @@ -61,7 +64,7 @@ object Implicits { class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs { assert(initctx.typer != null) - implicit val ctx: Context = initctx retractMode ImplicitsEnabled + implicit val ctx: Context = disableImplicits(initctx) val refs: List[TermRef] = companionRefs.toList flatMap (_.implicitMembers) /** The implicit references that are eligible for expected type `tp` */ @@ -76,8 +79,7 @@ object Implicits { * @param outerCtx the next outer context that makes visible further implicits */ class ContextualImplicits(val refs: List[TermRef], val outerCtx: Context)(initctx: Context) extends ImplicitRefs { - implicit val ctx: Context = - if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled + implicit val ctx: Context = disableImplicits(initctx) private val eligibleCache = new mutable.HashMap[Type, List[TermRef]] @@ -259,7 +261,7 @@ trait Implicits { self: Typer => * @param pos The position where errors should be reported. */ def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") { - ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}", show = true) { + ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", show = true) { val isearch = if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos) else new ImplicitSearch(pt, argument, pos) @@ -275,7 +277,7 @@ trait Implicits { self: Typer => /** An implicit search; parameters as in `inferImplicit` */ class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) { - val initctx: Context = ctx.fresh.withNewTyperState.retractMode(ImplicitsEnabled) + val initctx: Context = disableImplicits(ctx.fresh.withNewTyperState) def nestedContext = initctx.fresh.withNewTyperState protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches @@ -287,19 +289,18 @@ trait Implicits { self: Typer => def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = { /** Try to typecheck an implicit reference */ - def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = track("typedImplicit") { - val id = Ident(ref).withPos(pos) - val tree = - if (argument.isEmpty) - adapt(id, pt) - else - typed(untpd.Apply(untpd.TypedSplice(id), untpd.TypedSplice(argument) :: Nil), pt) + def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(s"typed implicit $ref, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", show = true) { + var generated: Tree = Ident(ref).withPos(pos) + if (!argument.isEmpty) + generated = typedUnadapted( + untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil), pt) + val generated1 = interpolateAndAdapt(generated, pt) lazy val shadowing = typed(untpd.Ident(ref.name), ref)(nestedContext).tpe if (ctx.typerState.reporter.hasErrors) nonMatchingImplicit(ref) else if (contextual && !(shadowing =:= ref)) shadowedImplicit(ref, shadowing) - else SearchSuccess(tree)(ref, ctx.typerState) - } + else SearchSuccess(generated)(ref, ctx.typerState) + }} /** Given a list of implicit references, produce a list of all implicit search successes, * where the first is supposed to be the best one. @@ -312,7 +313,7 @@ trait Implicits { self: Typer => case fail: SearchFailure => rankImplicits(pending1, acc) case best: SearchSuccess => - val newPending = pending filterNot (isAsGood(_, best.ref)) + val newPending = pending1 filterNot (isAsGood(_, best.ref)(nestedContext.withExploreTyperState)) rankImplicits(newPending, best :: acc) } case nil => acc diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 8d85c1911..cbce206ef 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -10,7 +10,7 @@ import annotation.unchecked import util.Positions._ import util.Stats import Decorators._ -import ErrorReporting.InfoString +import ErrorReporting.{errorType, InfoString} object Inferencing { @@ -113,7 +113,7 @@ object Inferencing { * any uninstantiated type variables, provided that * - the instance type for the variable is not Nothing or Null * - the overall result of `isFullYDefined` is `true`. - * Variables that are succesfully minimized do not count as uninstantiated. + * Variables that are successfully minimized do not count as uninstantiated. */ def isFullyDefined(tp: Type, forceIt: Boolean = false)(implicit ctx: Context): Boolean = { val nestedCtx = ctx.fresh.withNewTyperState @@ -122,6 +122,13 @@ object Inferencing { result } + def forceFullyDefined(tp: Type)(implicit ctx: Context): Boolean = + isFullyDefined(tp, forceIt = true) + + def fullyDefinedType(tp: Type, what: String, pos: Position)(implicit ctx: Context) = + if (forceFullyDefined(tp)) tp + else errorType(i"internal error: type of $what $tp is not fully defined", pos) + private class IsFullyDefinedAccumulator(forceIt: Boolean)(implicit ctx: Context) extends TypeAccumulator[Boolean] { def traverse(tp: Type): Boolean = apply(true, tp) def apply(x: Boolean, tp: Type) = !x || isOK(tp) && foldOver(x, tp) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 72ed20454..7582b0d0d 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -7,7 +7,7 @@ import ast._ import Trees._, Constants._, StdNames._, Scopes._, Denotations._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ import ast.desugar, ast.desugar._ -import Inferencing.AnySelectionProto +import Inferencing.{fullyDefinedType, AnySelectionProto} import util.Positions._ import util.SourcePosition import collection.mutable @@ -385,7 +385,9 @@ class Namer { typer: Typer => tp & itpe } } - inherited orElse typedAheadExpr(mdef.rhs).tpe.widen + def rhsType = interpolateAndAdapt(typedAheadExpr(mdef.rhs), WildcardType).tpe.widen + def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) + inherited orElse lhsType } paramFn(typedAheadType(mdef.tpt, pt).tpe) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 1e3049f7e..11de19648 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -458,7 +458,7 @@ class Typer extends Namer with Applications with Implicits { val result = cpy.Block(tree, stats1, expr1).withType(blockType(stats1, expr1.tpe)) val leaks = CheckTrees.escapingRefs(result) if (leaks.isEmpty) result - else if (isFullyDefined(pt)) { + else if (forceFullyDefined(pt)) { val expr2 = typed(untpd.Typed(untpd.TypedSplice(expr1), untpd.TypeTree(pt))) untpd.Block(stats1, expr2) withType expr2.tpe } else errorTree(result, @@ -493,7 +493,7 @@ class Typer extends Namer with Applications with Implicits { if (!param.tpt.isEmpty) param else { val paramType = - if (isFullyDefined(formal)) formal + if (forceFullyDefined(formal)) formal else errorType("missing parameter type", param.pos) cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(paramType), param.rhs) } @@ -521,12 +521,10 @@ class Typer extends Namer with Applications with Implicits { typed(desugar.makeCaseLambda(tree.cases) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) - val selType = - if (isFullyDefined(sel1.tpe)) sel1.tpe - else errorType("internal error: type of pattern selector is not fully defined", tree.pos) + val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos) /** gadtSyms = "all type parameters of enclosing methods that appear - * non-variantly in the selector type + * non-variantly in the selector type" */ val gadtSyms: Set[Symbol] = { val accu = new TypeAccumulator[Set[Symbol]] { @@ -863,11 +861,8 @@ class Typer extends Namer with Applications with Implicits { } def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = ctx.traceIndented (s"typing ${tree.show}", show = true) { - try { - val tree1 = typedUnadapted(tree, pt) - ctx.interpolateUndetVars(tree1.tpe.widen, tree1.pos) - adapt(tree1, pt) - } catch { + try interpolateAndAdapt(typedUnadapted(tree, pt), pt) + catch { case ex: FatalTypeError => errorTree(tree, ex.getMessage) } } @@ -927,6 +922,11 @@ class Typer extends Namer with Applications with Implicits { fallBack } + def interpolateAndAdapt(tree: Tree, pt: Type)(implicit ctx: Context) = { + ctx.interpolateUndetVars(tree.tpe.widen, tree.pos) + adapt(tree, pt) + } + /** (-1) For expressions with annotated types, let AnnotationCheckers decide what to do * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode) */ @@ -965,7 +965,7 @@ class Typer extends Namer with Applications with Implicits { * (14) When in mode EXPRmode, apply a view * If all this fails, error */ - def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = track("adapt") { ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", show = false) { + def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = track("adapt") { ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", show = true) { assert(pt.exists) @@ -1034,7 +1034,7 @@ class Typer extends Namer with Applications with Implicits { implicitArgError(i"no implicit argument of type $formal found for $where" + failure.postscript) } } - adapt(tpd.Apply(tree, args), pt) + adapt(tpd.Apply(tree, args), wtp.resultType) case wtp: MethodType if !pt.isInstanceOf[SingletonType] => if ((defn.isFunctionType(pt) || (pt eq AnyFunctionProto)) && !tree.symbol.isConstructor) @@ -1064,7 +1064,7 @@ class Typer extends Namer with Applications with Implicits { if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) => pt match { case SAMType(meth) - if wtp <:< meth.info.toFunctionType && isFullyDefined(pt, forceIt = false) => + if wtp <:< meth.info.toFunctionType && isFullyDefined(pt) => return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt) case _ => } -- cgit v1.2.3