From 3a503cf9a05e5dc6e3fbf06419b6ca5b8c6ed14e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Mar 2014 16:24:05 +0100 Subject: Made TypeBoundsTrees to be TypTrees so they are eliminated by PostTyperTransform --- src/dotty/tools/dotc/ast/Trees.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 17e2c3817..e0e64f06a 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -644,7 +644,7 @@ object Trees { /** >: lo <: hi */ case class TypeBoundsTree[-T >: Untyped] private[ast] (lo: Tree[T], hi: Tree[T]) - extends Tree[T] { + extends TypTree[T] { type ThisTree[-T >: Untyped] = TypeBoundsTree[T] } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 6cc3a226e..e6f566193 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -88,7 +88,6 @@ class Typer extends Namer with Applications with Implicits { if (reallyExists(mbr)) site.select(name, mbr) else { if (!site.isErroneous) { - typr.println(s"site = $site, baseClasses = ${site.baseClasses}") ctx.error( if (name == nme.CONSTRUCTOR) i"$site does not have a constructor" else i"$name is not a member of $site", pos) -- cgit v1.2.3 From ccb4f8afb7af71364c1b3d0f6565d686155a8e66 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 09:43:14 +0100 Subject: Fix problem in TermRef.alternatives Rewrap needs to produce alternatives with signatures. Otgerwise the new denotation will simply overwrite the old because both the overloaded TermRef and the alternative will hash to the same unique TermRef. --- src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f9c8aad5c..bb30d9a9c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1140,7 +1140,7 @@ object Types { override def isOverloaded(implicit ctx: Context) = denot.isOverloaded private def rewrap(sd: SingleDenotation)(implicit ctx: Context) = - TermRef(prefix, name, sd) + TermRef.withSig(prefix, name, sd.signature, sd) def alternatives(implicit ctx: Context): List[TermRef] = denot.alternatives map rewrap -- cgit v1.2.3 From 5b30038dd3561468a8f4a9a9f50e1a0208062de1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 09:46:07 +0100 Subject: Fix problem comparing overloaded TermRefs Overloaded TermRefs do not have an info, and consequently do not support =:=. Yet in Typer#checkNewOrShadowed we compared termrefs with =:=. This gives an exception if the termrefs are overloaded. The fix is to provide a new method isSameRef in TypeComparer which is called instead of =:= in Typer#checkNewOrShadowed. --- src/dotty/tools/dotc/core/TypeComparer.scala | 22 +++++++++++++++++++--- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index d46a8387f..ffb20cbad 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -715,9 +715,25 @@ class TypeComparer(initctx: Context) extends DotClass { /** Two types are the same if are mutual subtypes of each other */ def isSameType(tp1: Type, tp2: Type): Boolean = - if (tp1 == NoType || tp2 == NoType) false - else if (tp1 eq tp2) true - else isSubType(tp1, tp2) && isSubType(tp2, tp1) + isSubType(tp1, tp2) && isSubType(tp2, tp1) + + /** Same as `isSameType` but also can be applied to overloaded TermRefs, where + * two overloaded refs are the same if they have pairwise equal alternatives + */ + def isSameRef(tp1: Type, tp2: Type): Boolean = ctx.traceIndented(s"isSameRef($tp1, $tp2") { + def isSubRef(tp1: Type, tp2: Type): Boolean = tp1 match { + case tp1: TermRef if tp1.isOverloaded => + tp1.alternatives forall (isSubRef(_, tp2)) + case _ => + tp2 match { + case tp2: TermRef if tp2.isOverloaded => + tp2.alternatives exists (isSubRef(tp1, _)) + case _ => + isSubType(tp1, tp2) + } + } + isSubRef(tp1, tp2) && isSubRef(tp2, tp1) + } /** 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) /*<|<*/ { diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index e6f566193..aabcde94d 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -244,7 +244,7 @@ class Typer extends Namer with Applications with Implicits { * does properly shadow the new one from an outer context. */ def checkNewOrShadowed(found: Type, newPrec: Int): Type = - if (!previous.exists || (previous =:= found)) found + if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found else if ((prevCtx.scope eq ctx.scope) && (newPrec == definition || newPrec == namedImport && prevPrec == wildImport)) { -- cgit v1.2.3 From 9e25a624516186b606fcd734dd1b066e1204ca79 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 10:11:36 +0100 Subject: Fix of accidental omission in TypeComparer --- src/dotty/tools/dotc/core/TypeComparer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index ffb20cbad..da8263ac1 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -715,7 +715,9 @@ class TypeComparer(initctx: Context) extends DotClass { /** Two types are the same if are mutual subtypes of each other */ def isSameType(tp1: Type, tp2: Type): Boolean = - isSubType(tp1, tp2) && isSubType(tp2, tp1) + if (tp1 eq NoType) false + else if (tp1 eq tp2) true + else isSubType(tp1, tp2) && isSubType(tp2, tp1) /** Same as `isSameType` but also can be applied to overloaded TermRefs, where * two overloaded refs are the same if they have pairwise equal alternatives -- cgit v1.2.3 From af3bfbab17f6f13b10497487259138732d8b8bb9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 10:17:37 +0100 Subject: Removed useless ValDef flagged by @samuelgruetter in previous pull request --- src/dotty/tools/dotc/typer/Typer.scala | 3 +-- tests/pos/overloadedAccess.scala | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/pos/overloadedAccess.scala (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index aabcde94d..4c4920e5b 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -138,8 +138,7 @@ class Typer extends Namer with Applications with Implicits { } val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer - val addendum = - alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) + alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) if (!tpe.isError) ctx.error(i"$what cannot be accessed as a member of $pre$where.$whyNot", pos) ErrorType diff --git a/tests/pos/overloadedAccess.scala b/tests/pos/overloadedAccess.scala new file mode 100644 index 000000000..a2d72f583 --- /dev/null +++ b/tests/pos/overloadedAccess.scala @@ -0,0 +1,18 @@ +object overloadedAccess { + + trait ST { + def f(x: Object): Int = 1 + def f(x: Int): Unit = () + } + + object O extends ST { + def f(x: String): Unit = () + } + + class C extends ST { + import O._ // needs to pick inherited member because they are made visible in same scope. + val x = f("abc") + val y: Int = x + } + +} -- cgit v1.2.3 From 2669fac03190a4b52b120e07896cf5cd3de208c6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Mar 2014 18:36:38 +0100 Subject: First step towards Typer Reorganization. Goal is better modularization and avoiding code duplication and divergence between Typer and tpd. As a first step, we split Inferencing into Inferencing, Checking, and ProtoTypes. Inferencing and Checking become Typer traits, while ProtoTypes remains a global object. Eventually: - we want to have a SimpleTyper, which does the main stuff in tpd, and can mixin either full checking or no checking. Each method in SimpleTyper takes an untyped tree (which is assumed to have typed arguments) and adds a toplevel type to that tree. The methods subsume the type-checking parts in Typers, except for (1) simplifications and expansions (2) computing prototypes and recursing with them into childtrees (3) adaptation. The method calls the necessary checking operations, which may however be stubbed out. The idea is already exercised in the typechecking code for Literal and New, except that for now it calls methods in tpd (they will become methods in SimpleTyper instead). - Typer should inherit from SimpleTyper, and forward all logic except for (1) - (3) to it. - tpd should call the simple typer it gets from ctx.typer - ctx.typer should be a SimpleTyper, not a complete one. --- src/dotty/tools/dotc/ast/tpd.scala | 16 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 +- src/dotty/tools/dotc/typer/Applications.scala | 2 +- src/dotty/tools/dotc/typer/Checking.scala | 119 +++++ src/dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- src/dotty/tools/dotc/typer/Implicits.scala | 2 +- src/dotty/tools/dotc/typer/Inferencing.scala | 491 +-------------------- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/ProtoTypes.scala | 384 ++++++++++++++++ src/dotty/tools/dotc/typer/Typer.scala | 18 +- 10 files changed, 537 insertions(+), 503 deletions(-) create mode 100644 src/dotty/tools/dotc/typer/Checking.scala create mode 100644 src/dotty/tools/dotc/typer/ProtoTypes.scala (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 95f5b49be..3a0790c9c 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -68,7 +68,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } def Literal(const: Constant)(implicit ctx: Context): Literal = - untpd.Literal(const).withType(const.tpe).checked + typedLiteral(untpd.Literal(const)) + + def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = + tree.withType { + tree.const.tag match { + case UnitTag => defn.UnitType + case NullTag => defn.NullType + case _ => ConstantType(tree.const) + } + } def unitLiteral(implicit ctx: Context): Literal = Literal(Constant(())) @@ -76,6 +85,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def New(tpt: Tree)(implicit ctx: Context): New = untpd.New(tpt).withType(tpt.tpe).checked + def typedNew(tree: untpd.New)(implicit ctx: Context) = { + ctx.typer.checkClassTypeWithStablePrefix(tree.tpt.tpe, tree.tpt.pos, traitReq = false) + tree.withType(tree.tpt.tpe) + } + def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp)) def Pair(left: Tree, right: Tree)(implicit ctx: Context): Pair = diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2702143c1..5231ccd93 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -7,7 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annot import StdNames.nme import ast.{Trees, untpd} import typer.Namer -import typer.Inferencing.{SelectionProto, ViewProto} +import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto} import Trees._ import scala.annotation.switch @@ -120,7 +120,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case ExprType(result) => return "=> " ~ toText(result) - case typer.Inferencing.FunProto(args, resultType, _) => + case FunProto(args, resultType, _) => return "funproto(" ~ toTextGlobal(args, ", ") ~ "):" ~ toText(resultType) case _ => } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index ce3198c64..26325e57e 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -19,7 +19,7 @@ import ErrorReporting._ import Trees._ import Names._ import StdNames._ -import Inferencing._ +import ProtoTypes._ import EtaExpansion._ import collection.mutable import reflect.ClassTag diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala new file mode 100644 index 000000000..2fa0d5519 --- /dev/null +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -0,0 +1,119 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ +import Trees._ +import Constants._ +import Scopes._ +import annotation.unchecked +import util.Positions._ +import util.{Stats, SimpleMap} +import util.common._ +import Decorators._ +import Uniques._ +import ErrorReporting.{errorType, InfoString} +import config.Printers._ +import collection.mutable + +trait Checking { + + import tpd._ + + /** Check that type arguments `args` conform to corresponding bounds in `poly` */ + def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = + for ((arg, bounds) <- args zip poly.paramBounds) { + def notConforms(which: String, bound: Type) = + ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) + if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi) + if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) + } + + /** Check that type `tp` is stable. + * @return The type itself + */ + def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = + if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos) + + /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is + * false check that `tp` is a trait. + * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not. + */ + def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = + tp.underlyingClassRef match { + case tref: TypeRef => + checkStable(tref.prefix, pos) + if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos) + tp + case _ => + ctx.error(i"$tp is not a class type", pos) + defn.ObjectClass.typeRef + } + + /** Check that (return) type of implicit definition is not empty */ + def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { + case TypeTree(original) if original.isEmpty => + val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" + ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) + case _ => + } + + /** Check that a non-implicit parameter making up the first parameter section of an + * implicit conversion is not a singleton type. + */ + def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { + case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => + if (vparam.tpt.tpe.isInstanceOf[SingletonType]) + ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) + case _ => + } + + /** Check that any top-level type arguments in this type are feasible, i.e. that + * their lower bound conforms to their upper cound. If a type argument is + * infeasible, issue and error and continue with upper bound. + */ + def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { + case tp: RefinedType => + tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) + case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => + ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos) + tp.derivedTypeAlias(hi) + case _ => + tp + } + + /** Check that class does not define */ + def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { + val seen = new mutable.HashMap[Name, List[Symbol]] { + override def default(key: Name) = Nil + } + typr.println(i"check no double defs $cls") + for (decl <- cls.info.decls) { + for (other <- seen(decl.name)) { + typr.println(i"conflict? $decl $other") + if (decl.signature matches other.signature) { + def doubleDefError(decl: Symbol, other: Symbol): Unit = { + def ofType = if (decl.isType) "" else i": ${other.info}" + def explanation = + if (!decl.isSourceMethod) "" + else "\n (both definitions have the same erased type signature)" + ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos) + } + if (decl is Synthetic) doubleDefError(other, decl) + else doubleDefError(decl, other) + } + if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { + ctx.error(i"two or more overloaded variants of $decl have default arguments") + decl resetFlag HasDefaultParams + } + } + seen(decl.name) = decl :: seen(decl.name) + } + } + + def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { + ??? // to be done in later phase: check that class `cls` is legal in a new. + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 032190625..397285f6a 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -5,7 +5,7 @@ package typer import ast._ import core._ import Trees._ -import Types._, Inferencing._, Contexts._, Decorators._, Denotations._, Symbols._ +import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ import Applications._, Implicits._ import util.Positions._ import printing.Showable diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 008fe8966..b939c1b0d 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -20,8 +20,8 @@ import Decorators._ import Names._ import StdNames._ import Constants._ -import Inferencing._ import Applications._ +import ProtoTypes._ import ErrorReporting._ import Hashable._ import config.Config diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index ea3109afa..6d9afecab 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -8,6 +8,7 @@ import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps import Trees._ import Constants._ import Scopes._ +import ProtoTypes._ import annotation.unchecked import util.Positions._ import util.{Stats, SimpleMap} @@ -18,291 +19,10 @@ import ErrorReporting.{errorType, InfoString} import config.Printers._ import collection.mutable -object Inferencing { +trait Inferencing { this: Checking => import tpd._ - /** A trait defining an `isCompatible` method. */ - trait Compatibility { - - /** Is there an implicit conversion from `tp` to `pt`? */ - def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean - - /** A type `tp` is compatible with a type `pt` if one of the following holds: - * 1. `tp` is a subtype of `pt` - * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type - * 3. there is an implicit conversion from `tp` to `pt`. - */ - def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = - tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt) - - /** Test compatibility after normalization in a fresh typerstate. */ - def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.withExploreTyperState - isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx) - } - - /** Check that the result type of the current method - * fits the given expected result type. - */ - def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { - case FunProto(_, result, _) => - mt match { - case mt: MethodType => - mt.isDependent || constrainResult(mt.resultType, pt.resultType) - case _ => - true - } - case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) => - mt match { - case mt: MethodType => - mt.isDependent || isCompatible(normalize(mt, pt), pt) - case _ => - isCompatible(mt, pt) - } - case _ => - true - } - } - - object NoViewsAllowed extends Compatibility { - override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false - } - - /** A prototype for expressions [] that are part of a selection operation: - * - * [ ].name: proto - */ - abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) - extends CachedProxyType with ProtoType with ValueTypeOrProto { - - override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { - name == nme.WILDCARD || { - val mbr = tp1.member(name) - def qualifies(m: SingleDenotation) = compat.normalizedCompatible(m.info, memberProto) - mbr match { // hasAltWith inlined for performance - case mbr: SingleDenotation => mbr.exists && qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - } - } - - def underlying(implicit ctx: Context) = WildcardType - - def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = - if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this - else SelectionProto(name, memberProto, compat) - - override def equals(that: Any): Boolean = that match { - case that: SelectionProto => - (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) - case _ => - false - } - - def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) - - override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0) - } - - class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) - - object SelectionProto { - def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { - val selproto = new CachedSelectionProto(name, memberProto, compat) - if (compat eq NoViewsAllowed) unique(selproto) else selproto - } - } - - /** Create a selection proto-type, but only one level deep; - * treat constructors specially - */ - def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) = - if (name.isConstructorName) WildcardType - else tp match { - case tp: UnapplyFunProto => new UnapplySelectionProto(name) - case tp: ProtoType => SelectionProto(name, WildcardType, typer) - case _ => SelectionProto(name, tp, typer) - } - - /** A prototype for expressions [] that are in some unspecified selection operation - * - * [].?: ? - * - * Used to indicate that expression is in a context where the only valid - * operation is further selection. In this case, the expression need not be a value. - * @see checkValue - */ - object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) - - /** A prototype for selections in pattern constructors */ - class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) - - trait ApplyingProto extends ProtoType - - /** A prototype for expressions that appear in function position - * - * [](args): resultType - */ - case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) - extends UncachedGroundType with ApplyingProto { - private var myTypedArgs: List[Tree] = Nil - - /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ - private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty - - def isMatchedBy(tp: Type)(implicit ctx: Context) = - typer.isApplicable(tp, Nil, typedArgs, resultType) - - def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) = - if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this - else new FunProto(args, resultType, typer) - - def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty - - /** The typed arguments. This takes any arguments already typed using - * `typedArg` into account. - */ - def typedArgs: List[Tree] = { - if (!argsAreTyped) - myTypedArgs = args mapconserve { arg => - val targ = myTypedArg(arg) - if (targ != null) targ else typer.typed(arg) - } - myTypedArgs - } - - /** Type single argument and remember the unadapted result in `myTypedArg`. - * used to avoid repeated typings of trees when backtracking. - */ - def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = { - var targ = myTypedArg(arg) - if (targ == null) { - val counts = ctx.reporter.errorCounts - targ = typer.typedUnadapted(arg, formal) - if (ctx.reporter.wasSilent(counts)) - myTypedArg = myTypedArg.updated(arg, targ) - } - typer.adapt(targ, formal) - } - - override def toString = s"FunProto(${args mkString ","} => $resultType)" - - def map(tm: TypeMap)(implicit ctx: Context): FunProto = - derivedFunProto(args, tm(resultType), typer) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType) - } - - /** A prototype for implicitly inferred views: - * - * []: argType => resultType - */ - abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) - extends CachedGroundType with ApplyingProto { - def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ { - ctx.typer.isApplicable(tp, argType :: Nil, resultType) - } - - def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = - if ((argType eq this.argType) && (resultType eq this.resultType)) this - else ViewProto(argType, resultType) - - def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType) - - override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = - AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets - } - - class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) { - override def computeHash = doHash(argType, resultType) - } - - object ViewProto { - def apply(argType: Type, resultType: Type)(implicit ctx: Context) = - unique(new CachedViewProto(argType, resultType)) - } - - class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer) - - /** A prototype for expressions [] that are type-parameterized: - * - * [] [targs] resultType - */ - case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType { - override def isMatchedBy(tp: Type)(implicit ctx: Context) = { - def isInstantiatable(tp: Type) = tp.widen match { - case PolyType(paramNames) => paramNames.length == targs.length - case _ => false - } - isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info)) - } - - def derivedPolyProto(targs: List[Type], resultType: Type) = - if ((targs eq this.targs) && (resultType eq this.resultType)) this - else PolyProto(targs, resultType) - - def map(tm: TypeMap)(implicit ctx: Context): PolyProto = - derivedPolyProto(targs mapConserve tm, tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = - ta(ta.foldOver(x, targs), resultType) - } - - /** A prototype for expressions [] that are known to be functions: - * - * [] _ - */ - object AnyFunctionProto extends UncachedGroundType with ProtoType { - def isMatchedBy(tp: Type)(implicit ctx: Context) = true - def map(tm: TypeMap)(implicit ctx: Context) = this - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x - } - - /** The normalized form of a type - * - unwraps polymorphic types, tracking their parameters in the current constraint - * - skips implicit parameters - * - converts non-dependent method types to the corresponding function types - * - dereferences parameterless method types - * - dereferences nullary method types provided the corresponding function type - * is not a subtype of the expected type. - * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a - * def toString(): String - * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit - * insertion, with a nice explosion of inference search because of course every implicit result has some sort - * of toString method. The problem is solved by dereferencing nullary method types if the corresponding - * function type is not compatible with the prototype. - */ - def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") { - tp.widenSingleton match { - case poly: PolyType => normalize(constrained(poly).resultType, pt) - case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ => - if (mt.isImplicit) mt.resultType - else { - val rt = normalize(mt.resultType, pt) - if (pt.isInstanceOf[ApplyingProto]) - mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) - else { - val ft = defn.FunctionType(mt.paramTypes, rt) - if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt - } - } - case et: ExprType => et.resultType - case _ => tp - } - } - - /** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ - object ForceDegree extends Enumeration { - val none, // don't force type variables - noBottom, // force type variables, fail if forced to Nothing or Null - all = Value // force type variables, don't fail - } - /** Is type fully defined, meaning the type does not contain wildcard types * or uninstantiated type variables. As a side effect, this will minimize * any uninstantiated type variables, according to the given force degree, @@ -395,54 +115,6 @@ object Inferencing { case _ => NoType } - /** Check that type arguments `args` conform to corresponding bounds in `poly` */ - def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = - for ((arg, bounds) <- args zip poly.paramBounds) { - def notConforms(which: String, bound: Type) = - ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) - if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi) - if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) - } - - /** Check that type `tp` is stable. - * @return The type itself - */ - def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = - if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos) - - /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is - * false check that `tp` is a trait. - * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not. - */ - def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = - tp.underlyingClassRef match { - case tref: TypeRef => - checkStable(tref.prefix, pos) - if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos) - tp - case _ => - ctx.error(i"$tp is not a class type", pos) - defn.ObjectClass.typeRef - } - - /** Check that (return) type of implicit definition is not empty */ - def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { - case TypeTree(original) if original.isEmpty => - val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" - ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) - case _ => - } - - /** Check that a non-implicit parameter making up the first parameter section of an - * implicit conversion is not a singleton type. - */ - def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { - case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => - if (vparam.tpt.tpe.isInstanceOf[SingletonType]) - ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) - case _ => - } - /** Ensure that the first type in a list of parent types Ps points to a non-trait class. * If that's not already the case, add one. The added class type CT is determined as follows. * First, let C be the unique class such that @@ -484,134 +156,6 @@ object Inferencing { TypeTree(checkFeasible(first, pos, i"\n in inferred parent $first")).withPos(pos) :: parents } - /** Check that any top-level type arguments in this type are feasible, i.e. that - * their lower bound conforms to their upper cound. If a type argument is - * infeasible, issue and error and continue with upper bound. - */ - def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { - case tp: RefinedType => - tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) - case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => - ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos) - tp.derivedTypeAlias(hi) - case _ => - tp - } - - /** Check that class does not define */ - def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { - val seen = new mutable.HashMap[Name, List[Symbol]] { - override def default(key: Name) = Nil - } - typr.println(i"check no double defs $cls") - for (decl <- cls.info.decls) { - for (other <- seen(decl.name)) { - typr.println(i"conflict? $decl $other") - if (decl.signature matches other.signature) { - def doubleDefError(decl: Symbol, other: Symbol): Unit = { - def ofType = if (decl.isType) "" else i": ${other.info}" - def explanation = - if (!decl.isSourceMethod) "" - else "\n (both definitions have the same erased type signature)" - ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos) - } - if (decl is Synthetic) doubleDefError(other, decl) - else doubleDefError(decl, other) - } - if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { - ctx.error(i"two or more overloaded variants of $decl have default arguments") - decl resetFlag HasDefaultParams - } - } - seen(decl.name) = decl :: seen(decl.name) - } - } - - def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { - ??? // to be done in later phase: check that class `cls` is legal in a new. - } - - /** Approximate occurrences of parameter types and uninstantiated typevars - * by wildcard types. - */ - final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { - case tp: NamedType => // default case, inlined for speed - if (tp.symbol.isStatic) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap)) - case tp: RefinedType => // default case, inlined for speed - tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) - case tp: TypeBounds if tp.lo eq tp.hi => // default case, inlined for speed - tp.derivedTypeAlias(wildApprox(tp.lo, theMap)) - case PolyParam(pt, pnum) => - WildcardType(wildApprox(pt.paramBounds(pnum)).bounds) - case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) wildApprox(inst) - else ctx.typerState.constraint.at(tp.origin) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => WildcardType - } - case tp: AndType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - def wildBounds(tp: Type) = - if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) - else - tp.derivedAndType(tp1a, tp2a) - case tp: OrType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(tp1a.bounds | tp2a.bounds) - else - tp.derivedOrType(tp1a, tp2a) - case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) - case tp: ViewProto => - tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) - case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed - tp - case _ => - (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) - } - - private[Inferencing] class WildApproxMap(implicit ctx: Context) extends TypeMap { - def apply(tp: Type) = wildApprox(tp, this) - } - - /** Add all parameters in given polytype `pt` to the constraint's domain. - * If the constraint contains already some of these parameters in its domain, - * make a copy of the polytype and add the copy's type parameters instead. - * Return either the original polytype, or the copy, if one was made. - * Also, if `owningTree` is non-empty, add a type variable for each parameter. - * @return The added polytype, and the list of created type variables. - */ - def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { - val state = ctx.typerState - def howmany = if (owningTree.isEmpty) "no" else "some" - def committable = if (ctx.typerState.isCommittable) "committable" else "uncommittable" - assert(owningTree.isEmpty != ctx.typerState.isCommittable, - s"inconsistent: $howmany typevars were added to $committable constraint ${state.constraint}") - - def newTypeVars(pt: PolyType): List[TypeVar] = - for (n <- (0 until pt.paramNames.length).toList) - yield new TypeVar(PolyParam(pt, n), state, owningTree) - - val added = - if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) - else pt - val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) - state.constraint = state.constraint.add(added, tvars) - (added, tvars) - } - - /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ - def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 - /** Interpolate those undetermined type variables in the widened type of this tree * which are introduced by type application contained in the tree. * If such a variable appears covariantly in type `tp` or does not appear at all, @@ -665,31 +209,12 @@ object Inferencing { } result } - - 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 } -/* not needed right now - - def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match { - case formal :: formals1 => - actuals match { - case actual :: actuals1 => actual <:< formal && isSubTypes(actuals1, formals1) - case _ => false - } - case nil => - actuals.isEmpty - } +/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ +object ForceDegree extends Enumeration { + val none, // don't force type variables + noBottom, // force type variables, fail if forced to Nothing or Null + all = Value // force type variables, don't fail +} - def formalParameters[T](mtp: MethodType, actuals: List[T])(isRepeated: T => Boolean)(implicit ctx: Context) = - if (mtp.isVarArgs && !(actuals.nonEmpty && isRepeated(actuals.last))) { - val leading = mtp.paramTypes.init - val repeated = mtp.paramTypes.last.typeArgs.head - val trailing = List.fill(actuals.length - leading.length)(repeated) - leading ++ trailing - } - else mtp.paramTypes - */ \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 463b7e71e..7d1e0629e 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._ +import ProtoTypes._ import util.Positions._ import util.{Attachment, SourcePosition, DotClass} import collection.mutable diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala new file mode 100644 index 000000000..87c556fdc --- /dev/null +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -0,0 +1,384 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ +import Trees._ +import Constants._ +import Scopes._ +import annotation.unchecked +import util.Positions._ +import util.{Stats, SimpleMap} +import util.common._ +import Decorators._ +import Uniques._ +import ErrorReporting.{errorType, InfoString} +import config.Printers._ +import collection.mutable + +object ProtoTypes { + + import tpd._ + + /** A trait defining an `isCompatible` method. */ + trait Compatibility { + + /** Is there an implicit conversion from `tp` to `pt`? */ + def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean + + /** A type `tp` is compatible with a type `pt` if one of the following holds: + * 1. `tp` is a subtype of `pt` + * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type + * 3. there is an implicit conversion from `tp` to `pt`. + */ + def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = + tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt) + + /** Test compatibility after normalization in a fresh typerstate. */ + def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { + val nestedCtx = ctx.fresh.withExploreTyperState + isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx) + } + + /** Check that the result type of the current method + * fits the given expected result type. + */ + def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { + case FunProto(_, result, _) => + mt match { + case mt: MethodType => + mt.isDependent || constrainResult(mt.resultType, pt.resultType) + case _ => + true + } + case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) => + mt match { + case mt: MethodType => + mt.isDependent || isCompatible(normalize(mt, pt), pt) + case _ => + isCompatible(mt, pt) + } + case _ => + true + } + } + + object NoViewsAllowed extends Compatibility { + override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false + } + + /** A prototype for expressions [] that are part of a selection operation: + * + * [ ].name: proto + */ + abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) + extends CachedProxyType with ProtoType with ValueTypeOrProto { + + override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { + name == nme.WILDCARD || { + val mbr = tp1.member(name) + def qualifies(m: SingleDenotation) = compat.normalizedCompatible(m.info, memberProto) + mbr match { // hasAltWith inlined for performance + case mbr: SingleDenotation => mbr.exists && qualifies(mbr) + case _ => mbr hasAltWith qualifies + } + } + } + + def underlying(implicit ctx: Context) = WildcardType + + def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = + if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this + else SelectionProto(name, memberProto, compat) + + override def equals(that: Any): Boolean = that match { + case that: SelectionProto => + (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) + case _ => + false + } + + def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) + + override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0) + } + + class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) + + object SelectionProto { + def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { + val selproto = new CachedSelectionProto(name, memberProto, compat) + if (compat eq NoViewsAllowed) unique(selproto) else selproto + } + } + + /** Create a selection proto-type, but only one level deep; + * treat constructors specially + */ + def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) = + if (name.isConstructorName) WildcardType + else tp match { + case tp: UnapplyFunProto => new UnapplySelectionProto(name) + case tp: ProtoType => SelectionProto(name, WildcardType, typer) + case _ => SelectionProto(name, tp, typer) + } + + /** A prototype for expressions [] that are in some unspecified selection operation + * + * [].?: ? + * + * Used to indicate that expression is in a context where the only valid + * operation is further selection. In this case, the expression need not be a value. + * @see checkValue + */ + object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) + + /** A prototype for selections in pattern constructors */ + class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) + + trait ApplyingProto extends ProtoType + + /** A prototype for expressions that appear in function position + * + * [](args): resultType + */ + case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) + extends UncachedGroundType with ApplyingProto { + private var myTypedArgs: List[Tree] = Nil + + /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ + private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty + + def isMatchedBy(tp: Type)(implicit ctx: Context) = + typer.isApplicable(tp, Nil, typedArgs, resultType) + + def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) = + if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this + else new FunProto(args, resultType, typer) + + def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty + + /** The typed arguments. This takes any arguments already typed using + * `typedArg` into account. + */ + def typedArgs: List[Tree] = { + if (!argsAreTyped) + myTypedArgs = args mapconserve { arg => + val targ = myTypedArg(arg) + if (targ != null) targ else typer.typed(arg) + } + myTypedArgs + } + + /** Type single argument and remember the unadapted result in `myTypedArg`. + * used to avoid repeated typings of trees when backtracking. + */ + def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = { + var targ = myTypedArg(arg) + if (targ == null) { + val counts = ctx.reporter.errorCounts + targ = typer.typedUnadapted(arg, formal) + if (ctx.reporter.wasSilent(counts)) + myTypedArg = myTypedArg.updated(arg, targ) + } + typer.adapt(targ, formal) + } + + override def toString = s"FunProto(${args mkString ","} => $resultType)" + + def map(tm: TypeMap)(implicit ctx: Context): FunProto = + derivedFunProto(args, tm(resultType), typer) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType) + } + + /** A prototype for implicitly inferred views: + * + * []: argType => resultType + */ + abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) + extends CachedGroundType with ApplyingProto { + def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ { + ctx.typer.isApplicable(tp, argType :: Nil, resultType) + } + + def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = + if ((argType eq this.argType) && (resultType eq this.resultType)) this + else ViewProto(argType, resultType) + + def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType) + + override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = + AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets + } + + class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) { + override def computeHash = doHash(argType, resultType) + } + + object ViewProto { + def apply(argType: Type, resultType: Type)(implicit ctx: Context) = + unique(new CachedViewProto(argType, resultType)) + } + + class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto( + untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer) + + /** A prototype for expressions [] that are type-parameterized: + * + * [] [targs] resultType + */ + case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType { + override def isMatchedBy(tp: Type)(implicit ctx: Context) = { + def isInstantiatable(tp: Type) = tp.widen match { + case PolyType(paramNames) => paramNames.length == targs.length + case _ => false + } + isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info)) + } + + def derivedPolyProto(targs: List[Type], resultType: Type) = + if ((targs eq this.targs) && (resultType eq this.resultType)) this + else PolyProto(targs, resultType) + + def map(tm: TypeMap)(implicit ctx: Context): PolyProto = + derivedPolyProto(targs mapConserve tm, tm(resultType)) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = + ta(ta.foldOver(x, targs), resultType) + } + + /** A prototype for expressions [] that are known to be functions: + * + * [] _ + */ + object AnyFunctionProto extends UncachedGroundType with ProtoType { + def isMatchedBy(tp: Type)(implicit ctx: Context) = true + def map(tm: TypeMap)(implicit ctx: Context) = this + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x + } + + /** Add all parameters in given polytype `pt` to the constraint's domain. + * If the constraint contains already some of these parameters in its domain, + * make a copy of the polytype and add the copy's type parameters instead. + * Return either the original polytype, or the copy, if one was made. + * Also, if `owningTree` is non-empty, add a type variable for each parameter. + * @return The added polytype, and the list of created type variables. + */ + def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { + val state = ctx.typerState + def howmany = if (owningTree.isEmpty) "no" else "some" + def committable = if (ctx.typerState.isCommittable) "committable" else "uncommittable" + assert(owningTree.isEmpty != ctx.typerState.isCommittable, + s"inconsistent: $howmany typevars were added to $committable constraint ${state.constraint}") + + def newTypeVars(pt: PolyType): List[TypeVar] = + for (n <- (0 until pt.paramNames.length).toList) + yield new TypeVar(PolyParam(pt, n), state, owningTree) + + val added = + if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) + else pt + val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) + state.constraint = state.constraint.add(added, tvars) + (added, tvars) + } + + /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ + def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 + + /** The normalized form of a type + * - unwraps polymorphic types, tracking their parameters in the current constraint + * - skips implicit parameters + * - converts non-dependent method types to the corresponding function types + * - dereferences parameterless method types + * - dereferences nullary method types provided the corresponding function type + * is not a subtype of the expected type. + * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a + * def toString(): String + * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit + * insertion, with a nice explosion of inference search because of course every implicit result has some sort + * of toString method. The problem is solved by dereferencing nullary method types if the corresponding + * function type is not compatible with the prototype. + */ + def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") { + tp.widenSingleton match { + case poly: PolyType => normalize(constrained(poly).resultType, pt) + case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ => + if (mt.isImplicit) mt.resultType + else { + val rt = normalize(mt.resultType, pt) + if (pt.isInstanceOf[ApplyingProto]) + mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) + else { + val ft = defn.FunctionType(mt.paramTypes, rt) + if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt + } + } + case et: ExprType => et.resultType + case _ => tp + } + } + + /** Approximate occurrences of parameter types and uninstantiated typevars + * by wildcard types. + */ + final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { + case tp: NamedType => // default case, inlined for speed + if (tp.symbol.isStatic) tp + else tp.derivedSelect(wildApprox(tp.prefix, theMap)) + case tp: RefinedType => // default case, inlined for speed + tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) + case tp: TypeBounds if tp.lo eq tp.hi => // default case, inlined for speed + tp.derivedTypeAlias(wildApprox(tp.lo, theMap)) + case PolyParam(pt, pnum) => + WildcardType(wildApprox(pt.paramBounds(pnum)).bounds) + case MethodParam(mt, pnum) => + WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) + case tp: TypeVar => + val inst = tp.instanceOpt + if (inst.exists) wildApprox(inst) + else ctx.typerState.constraint.at(tp.origin) match { + case bounds: TypeBounds => wildApprox(WildcardType(bounds)) + case NoType => WildcardType + } + case tp: AndType => + val tp1a = wildApprox(tp.tp1) + val tp2a = wildApprox(tp.tp2) + def wildBounds(tp: Type) = + if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) + else + tp.derivedAndType(tp1a, tp2a) + case tp: OrType => + val tp1a = wildApprox(tp.tp1) + val tp2a = wildApprox(tp.tp2) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(tp1a.bounds | tp2a.bounds) + else + tp.derivedOrType(tp1a, tp2a) + case tp: SelectionProto => + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) + case tp: ViewProto => + tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) + case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed + tp + case _ => + (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) + } + + private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap { + def apply(tp: Type) = wildApprox(tp, this) + } + + 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 +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4c4920e5b..12d349ce4 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -9,7 +9,7 @@ import Constants._ import StdNames._ import Scopes._ import Denotations._ -import Inferencing._ +import ProtoTypes._ import Contexts._ import Symbols._ import Types._ @@ -20,7 +20,6 @@ import NameOps._ import Flags._ import Decorators._ import ErrorReporting._ -import Inferencing.{FunProto, PolyProto, Compatibility, normalize} import EtaExpansion.etaExpand import util.Positions._ import util.common._ @@ -49,7 +48,7 @@ object Typer { } } -class Typer extends Namer with Applications with Implicits { +class Typer extends Namer with Applications with Implicits with Inferencing with Checking { import Typer._ import tpd.{cpy => _, _} @@ -414,13 +413,7 @@ class Typer extends Namer with Applications with Implicits { } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { - tree.withType { - tree.const.tag match { - case UnitTag => defn.UnitType - case NullTag => defn.NullType - case _ => ConstantType(tree.const) - } - } + tpd.typedLiteral(tree) } def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") { @@ -431,10 +424,9 @@ class Typer extends Namer with Applications with Implicits { val clsDef = TypeDef(Modifiers(Final), x, templ) typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => - val tpt1 = typedType(tree.tpt) - val clsref = checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + val tpt1 = typedType(tree.tpt) + tpd.typedNew(cpy.New(tree, tpt1)) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) - cpy.New(tree, tpt1).withType(tpt1.tpe) } } -- cgit v1.2.3 From dbd5a4d22b6164b708a87b508d9b9f135b44a3ee Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 11:25:57 +0100 Subject: Scond step to typer reorg: Introduce TypeAssigners. TypeAssigners assign a toplevel type to a node. They are mixed into Typer, and can be accessed from tpd using ctx.typeAssigner. --- src/dotty/tools/dotc/ast/tpd.scala | 20 ++++---------------- src/dotty/tools/dotc/core/Contexts.scala | 13 ++++++++----- src/dotty/tools/dotc/typer/Checking.scala | 27 +++++++++++++++++++-------- src/dotty/tools/dotc/typer/Namer.scala | 18 ++++++++++-------- src/dotty/tools/dotc/typer/ProtoTypes.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 27 +++++++++++++++++++++++++++ src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- 7 files changed, 72 insertions(+), 41 deletions(-) create mode 100644 src/dotty/tools/dotc/typer/TypeAssigner.scala (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3a0790c9c..32a36bbd1 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -11,6 +11,8 @@ import config.Printers._ /** Some creators for typed trees */ object tpd extends Trees.Instance[Type] with TypedTreeInfo { + private def ta(implicit ctx: Context) = ctx.typeAssigner + def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = Modifiers( sym.flags & ModifierFlags, if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY, @@ -68,27 +70,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } def Literal(const: Constant)(implicit ctx: Context): Literal = - typedLiteral(untpd.Literal(const)) - - def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = - tree.withType { - tree.const.tag match { - case UnitTag => defn.UnitType - case NullTag => defn.NullType - case _ => ConstantType(tree.const) - } - } + ta.assignType(untpd.Literal(const)) def unitLiteral(implicit ctx: Context): Literal = Literal(Constant(())) def New(tpt: Tree)(implicit ctx: Context): New = - untpd.New(tpt).withType(tpt.tpe).checked - - def typedNew(tree: untpd.New)(implicit ctx: Context) = { - ctx.typer.checkClassTypeWithStablePrefix(tree.tpt.tpe, tree.tpt.pos, traitReq = false) - tree.withType(tree.tpt.tpe) - } + ta.assignType(untpd.New(tpt)) def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp)) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index dd96023d7..9be2e2f43 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -119,10 +119,11 @@ object Contexts { protected def scope_=(scope: Scope) = _scope = scope def scope: Scope = _scope - /** The current typer */ - private[this] var _typer: Typer = _ - protected def typer_=(typer: Typer) = _typer = typer - def typer: Typer = _typer + /** The current type assigner ot typer */ + private[this] var _typeAssigner: TypeAssigner = _ + protected def typeAssigner_=(typeAssigner: TypeAssigner) = _typeAssigner = typeAssigner + def typeAssigner: TypeAssigner = _typeAssigner + def typer: Typer = _typeAssigner.asInstanceOf[Typer] /** The currently active import info */ private[this] var _importInfo: ImportInfo = _ @@ -312,7 +313,8 @@ object Contexts { def withTree(tree: Tree[_ >: Untyped]): this.type = { this.tree = tree; this } def withScope(scope: Scope): this.type = { this.scope = scope; this } def withNewScope: this.type = { this.scope = newScope; this } - def withTyper(typer: Typer): this.type = { this.typer = typer; this.scope = typer.scope; this } + def withTypeAssigner(typeAssigner: TypeAssigner): this.type = { this.typeAssigner = typeAssigner; this } + def withTyper(typer: Typer): this.type = { this.scope = typer.scope; withTypeAssigner(typer) } def withImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this } def withRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this } def withDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this } @@ -342,6 +344,7 @@ object Contexts { owner = NoSymbol sstate = settings.defaultState tree = untpd.EmptyTree + typeAssigner = TypeAssigner runInfo = new RunInfo(this) diagnostics = None moreProperties = Map.empty diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 2fa0d5519..5c21584bd 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -18,12 +18,23 @@ import ErrorReporting.{errorType, InfoString} import config.Printers._ import collection.mutable -trait Checking { +trait NoChecking { + import tpd._ + def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = () + def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () + def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp + def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = () + def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () + def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp + def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () +} + +trait Checking extends NoChecking { import tpd._ /** Check that type arguments `args` conform to corresponding bounds in `poly` */ - def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = + override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = for ((arg, bounds) <- args zip poly.paramBounds) { def notConforms(which: String, bound: Type) = ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) @@ -34,14 +45,14 @@ trait Checking { /** Check that type `tp` is stable. * @return The type itself */ - def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = + override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos) /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is * false check that `tp` is a trait. * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not. */ - def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = + override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp.underlyingClassRef match { case tref: TypeRef => checkStable(tref.prefix, pos) @@ -53,7 +64,7 @@ trait Checking { } /** Check that (return) type of implicit definition is not empty */ - def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { + override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { case TypeTree(original) if original.isEmpty => val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) @@ -63,7 +74,7 @@ trait Checking { /** Check that a non-implicit parameter making up the first parameter section of an * implicit conversion is not a singleton type. */ - def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { + override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => if (vparam.tpt.tpe.isInstanceOf[SingletonType]) ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) @@ -74,7 +85,7 @@ trait Checking { * their lower bound conforms to their upper cound. If a type argument is * infeasible, issue and error and continue with upper bound. */ - def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { + override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { case tp: RefinedType => tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => @@ -85,7 +96,7 @@ trait Checking { } /** Check that class does not define */ - def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { + override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { val seen = new mutable.HashMap[Name, List[Symbol]] { override def default(key: Name) = Nil } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 7d1e0629e..6e91e4754 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -58,14 +58,16 @@ trait NamerContextOps { this: Context => /** The symbol (stored in some typer's symTree) of an enclosing context definition */ def symOfContextTree(tree: untpd.Tree) = { def go(ctx: Context): Symbol = { - val typer = ctx.typer - if (typer == null) NoSymbol - else tree.getAttachment(typer.SymOfTree) match { - case Some(sym) => sym - case None => - var cx = ctx.outer - while (cx.typer eq typer) cx = cx.outer - go(cx) + ctx.typeAssigner match { + case typer: Typer => + tree.getAttachment(typer.SymOfTree) match { + case Some(sym) => sym + case None => + var cx = ctx.outer + while (cx.typeAssigner eq typer) cx = cx.outer + go(cx) + } + case _ => NoSymbol } } go(this) diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 87c556fdc..b4068408b 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -103,7 +103,7 @@ object ProtoTypes { def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) - override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0) + override def computeHash = addDelta(doHash(name, memberProto), if (compat eq NoViewsAllowed) 1 else 0) } class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala new file mode 100644 index 000000000..e711ee949 --- /dev/null +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -0,0 +1,27 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Scopes._, Contexts._, Constants._, Types._, Symbols._ + +trait TypeAssigner extends NoChecking { + + def assignType(tree: untpd.New)(implicit ctx: Context) = { + checkClassTypeWithStablePrefix(tree.tpt.tpe, tree.tpt.pos, traitReq = false) + tree.withType(tree.tpt.tpe) + } + + def assignType(tree: untpd.Literal)(implicit ctx: Context) = + tree.withType { + tree.const.tag match { + case UnitTag => defn.UnitType + case NullTag => defn.NullType + case _ => ConstantType(tree.const) + } + } +} + +object TypeAssigner extends TypeAssigner + diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 12d349ce4..c545641a0 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -48,7 +48,7 @@ object Typer { } } -class Typer extends Namer with Applications with Implicits with Inferencing with Checking { +class Typer extends Namer with TypeAssigner with Applications with Implicits with Inferencing with Checking { import Typer._ import tpd.{cpy => _, _} @@ -413,7 +413,7 @@ class Typer extends Namer with Applications with Implicits with Inferencing with } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { - tpd.typedLiteral(tree) + assignType(tree) } def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") { @@ -425,7 +425,7 @@ class Typer extends Namer with Applications with Implicits with Inferencing with typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => val tpt1 = typedType(tree.tpt) - tpd.typedNew(cpy.New(tree, tpt1)) + assignType(cpy.New(tree, tpt1)) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } } -- cgit v1.2.3 From 6aa88d6dfe501a695183761c2b5f4bd201cdf2c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Mar 2014 19:01:42 +0100 Subject: Main Typer reorg. Common code between tpd and Typer has been factored out into class TypeAssigner. --- src/dotty/tools/dotc/ast/CheckTrees.scala | 5 +- src/dotty/tools/dotc/ast/tpd.scala | 218 ++++---------- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 4 +- src/dotty/tools/dotc/typer/Applications.scala | 41 ++- src/dotty/tools/dotc/typer/Checking.scala | 13 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 317 ++++++++++++++++++++- src/dotty/tools/dotc/typer/Typer.scala | 266 +++++------------ 8 files changed, 472 insertions(+), 394 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala index 1fae0a1f1..832544706 100644 --- a/src/dotty/tools/dotc/ast/CheckTrees.scala +++ b/src/dotty/tools/dotc/ast/CheckTrees.scala @@ -6,6 +6,7 @@ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ +// TODO: revise, integrate in a checking phase. object CheckTrees { import tpd._ @@ -19,7 +20,7 @@ object CheckTrees { def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { var hoisted: Set[Symbol] = Set() - lazy val locals = localSyms(block.stats).toSet + lazy val locals = ctx.typeAssigner.localSyms(block.stats).toSet def isLocal(sym: Symbol): Boolean = (locals contains sym) && !isHoistableClass(sym) def isHoistableClass(sym: Symbol) = @@ -177,7 +178,7 @@ object CheckTrees { checkRefinements(forbidden - rsym, rs1) case nil => } - checkRefinements(localSyms(refinements).toSet, refinements) + checkRefinements(ctx.typeAssigner.localSyms(refinements).toSet, refinements) case AppliedTypeTree(tpt, args) => check(tpt.isValueType) val tparams = tpt.tpe.typeParams diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 32a36bbd1..bda90b9e6 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -19,55 +19,35 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { sym.annotations map (_.tree)) def Ident(tp: NamedType)(implicit ctx: Context): Ident = - underlyingIfRepeated(untpd.Ident(tp.name) withType tp).checked + ta.assignType(untpd.Ident(tp.name), tp) def Select(qualifier: Tree, name: Name)(implicit ctx: Context): Select = - untpd.Select(qualifier, name).withType(qualifier.tpe select name) + ta.assignType(untpd.Select(qualifier, name), qualifier) def Select(qualifier: Tree, tp: NamedType)(implicit ctx: Context): Select = - untpd.Select(qualifier, tp.name).withType(tp).checked + untpd.Select(qualifier, tp.name).withType(tp) def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit ctx: Context) = untpd.SelectWithSig(qualifier, name, sig) .withType(TermRef.withSig(qualifier.tpe, name.asTermName, sig)) + def SelectFromTypeTree(qualifier: Tree, name: Name)(implicit ctx: Context): SelectFromTypeTree = + ta.assignType(untpd.SelectFromTypeTree(qualifier, name), qualifier) + + def SelectFromTypeTree(qualifier: Tree, tp: NamedType)(implicit ctx: Context): SelectFromTypeTree = + untpd.SelectFromTypeTree(qualifier, tp.name).withType(tp) + def This(cls: ClassSymbol)(implicit ctx: Context): This = - untpd.This(cls.name).withType(cls.thisType).checked - - def Super(qual: Tree, mix: TypeName)(implicit ctx: Context): Super = { - val owntype = - if (mix.isEmpty) ctx.typeComparer.glb(qual.tpe.parents) - else { - val mixParents = qual.tpe.parents filter (_.name == mix) - check(mixParents.length == 1) - mixParents.head - } - untpd.Super(qual, mix).withType(SuperType(qual.tpe, owntype)).checked - } + ta.assignType(untpd.This(cls.name)) - def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = { - val owntype = fn.tpe.widen match { - case fntpe @ MethodType(pnames, ptypes) => - check(sameLength(ptypes, args), s"${fn.show}: ${fntpe.show} to ${args.map(_.show).mkString(", ")}") - fntpe.instantiate(args map (_.tpe)) - case t => - check(false, s"fn = $fn, args = $args, tp = $t") - ErrorType - } - untpd.Apply(fn, args).withType(owntype).checked - } + def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean)(implicit ctx: Context): Super = + ta.assignType(untpd.Super(qual, mix), qual, inConstrCall) - def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = { - val owntype = fn.tpe.widen match { - case fntpe @ PolyType(pnames) => - check(sameLength(pnames, args)) - fntpe.instantiate(args map (_.tpe)) - case t => - check(false, s"bad type: $t") - ErrorType - } - untpd.TypeApply(fn, args).withType(owntype).checked - } + def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = + ta.assignType(untpd.Apply(fn, args), fn, args) + + def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = + ta.assignType(untpd.TypeApply(fn, args), fn, args) def Literal(const: Constant)(implicit ctx: Context): Literal = ta.assignType(untpd.Literal(const)) @@ -76,70 +56,33 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Literal(Constant(())) def New(tpt: Tree)(implicit ctx: Context): New = - ta.assignType(untpd.New(tpt)) + ta.assignType(untpd.New(tpt), tpt) def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp)) def Pair(left: Tree, right: Tree)(implicit ctx: Context): Pair = - untpd.Pair(left, right).withType(defn.PairType.appliedTo(left.tpe, right.tpe)).checked + ta.assignType(untpd.Pair(left, right), left, right) def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = - untpd.Typed(expr, tpt).withType(tpt.tpe).checked + ta.assignType(untpd.Typed(expr, tpt), tpt) def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) = - untpd.NamedArg(name, arg).withType(arg.tpe).checked + ta.assignType(untpd.NamedArg(name, arg), arg) def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = - untpd.Assign(lhs, rhs).withType(defn.UnitType).checked + ta.assignType(untpd.Assign(lhs, rhs)) def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = - untpd.Block(stats, expr).withType(avoid(expr.tpe, localSyms(stats))).checked - - def avoid(tp: Type, syms: => List[Symbol])(implicit ctx: Context): Type = { - val widenMap = new TypeMap { - lazy val forbidden = syms.toSet - def toAvoid(tp: Type): Boolean = tp match { - case tp: TermRef => - val sym = tp.symbol - sym.exists && ( - sym.owner.isTerm && (forbidden contains sym) - || !(sym.owner is Package) && toAvoid(tp.prefix) - ) - case _ => - false - } - def apply(tp: Type) = tp match { - case tp: TermRef if toAvoid(tp) && variance > 0 => - apply(tp.info) - case tp: TypeRef if toAvoid(tp.prefix) => - tp.info match { - case TypeAlias(ref) => apply(ref) - case _ => mapOver(tp) - } - case tp: RefinedType => - val tp1 @ RefinedType(parent1, _) = mapOver(tp) - if (tp1.refinedInfo existsPart toAvoid) { - typr.println(s"dropping refinement from $tp1") - parent1 - } - else tp1 - case _ => - mapOver(tp) - } - } - widenMap(tp) - } + ta.assignType(untpd.Block(stats, expr), stats, expr) def maybeBlock(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree = if (stats.isEmpty) expr else Block(stats, expr) def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = - untpd.If(cond, thenp, elsep).withType(thenp.tpe | elsep.tpe).checked + ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep) - def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = { - val ownType = if (tpt.isEmpty) meth.tpe.widen.toFunctionType else tpt.tpe - untpd.Closure(env, meth, tpt).withType(ownType).checked - } + def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = + ta.assignType(untpd.Closure(env, meth, tpt), meth, tpt) /** A function def * @@ -159,37 +102,30 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Closure(Nil, Ident(TermRef(NoPrefix, meth)), targetTpt)) } - def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = - untpd.Match(selector, cases).withType(ctx.typeComparer.lub(cases.tpes)).checked - def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = - untpd.CaseDef(pat, guard, body).withType(body.tpe).checked + ta.assignType(untpd.CaseDef(pat, guard, body), body) + + def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = + ta.assignType(untpd.Match(selector, cases), cases) def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return = - untpd.Return(expr, from).withType(defn.NothingType).checked + ta.assignType(untpd.Return(expr, from)) def Try(block: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = - untpd.Try(block, handler, finalizer).withType(block.tpe | handler.tpe).checked + ta.assignType(untpd.Try(block, handler, finalizer), block, handler) def Throw(expr: Tree)(implicit ctx: Context): Throw = - untpd.Throw(expr).withType(defn.NothingType).checked + ta.assignType(untpd.Throw(expr)) def SeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral = - untpd.SeqLiteral(elems) - .withType(defn.SeqClass.typeRef.appliedTo(ctx.typeComparer.lub(elems.tpes))) - .checked - - def SeqLiteral(tpe: Type, elems: List[Tree])(implicit ctx: Context): SeqLiteral = { - val untpdSeqLit = - if (tpe derivesFrom defn.SeqClass) untpd.SeqLiteral(elems) - else untpd.JavaSeqLiteral(elems) - untpdSeqLit.withType(tpe).checked - } + ta.assignType(untpd.SeqLiteral(elems), elems) + + def SeqLiteral(tpe: Type, elems: List[Tree])(implicit ctx: Context): SeqLiteral = + if (tpe derivesFrom defn.SeqClass) SeqLiteral(elems) else JavaSeqLiteral(elems) def JavaSeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral = new untpd.JavaSeqLiteral(elems) .withType(defn.ArrayClass.typeRef.appliedTo(ctx.typeComparer.lub(elems.tpes))) - .checked def TypeTree(original: Tree)(implicit ctx: Context): TypeTree = TypeTree(original.tpe, original) @@ -198,61 +134,41 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { untpd.TypeTree(original).withType(tp).checked def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree = - untpd.SingletonTypeTree(ref).withType(ref.tpe).checked - - def SelectFromTypeTree(qualifier: Tree, name: Name)(implicit ctx: Context): SelectFromTypeTree = - untpd.SelectFromTypeTree(qualifier, name).withType(qualifier.tpe select name).checked - - def SelectFromTypeTree(qualifier: Tree, tp: NamedType)(implicit ctx: Context): SelectFromTypeTree = - untpd.SelectFromTypeTree(qualifier, tp.name).withType(tp).checked + ta.assignType(untpd.SingletonTypeTree(ref), ref) def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree = - untpd.AndTypeTree(left, right).withType(left.tpe & right.tpe).checked + ta.assignType(untpd.AndTypeTree(left, right), left, right) def OrTypeTree(left: Tree, right: Tree)(implicit ctx: Context): OrTypeTree = - untpd.OrTypeTree(left, right).withType(left.tpe | right.tpe).checked + ta.assignType(untpd.OrTypeTree(left, right), left, right) - def RefinedTypeTree(tpt: Tree, refinements: List[Tree])(implicit ctx: Context): RefinedTypeTree = { - def refineType(tp: Type, refinement: Symbol): Type = - RefinedType(tp, refinement.name, refinement.info) - untpd.RefinedTypeTree(tpt, refinements) - .withType((tpt.tpe /: (refinements map (_.symbol)))(refineType)).checked - } - - def refineType(tp: Type, refinement: Symbol)(implicit ctx: Context): Type = - RefinedType(tp, refinement.name, refinement.info) + // RefinedTypeTree is missing, handled specially in Typer and Unpickler. - def AppliedTypeTree(tpt: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree = - untpd.AppliedTypeTree(tpt, args).withType(tpt.tpe.appliedTo(args map (_.tpe))).checked + def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree = + ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args) def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree = - untpd.ByNameTypeTree(result).withType(ExprType(result.tpe)).checked + ta.assignType(untpd.ByNameTypeTree(result), result) def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree = - untpd.TypeBoundsTree(lo, hi).withType(TypeBounds(lo.tpe, hi.tpe)).checked + ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi) def Bind(sym: TermSymbol, body: Tree)(implicit ctx: Context): Bind = - untpd.Bind(sym.name, body).withType(sym.termRef).checked + ta.assignType(untpd.Bind(sym.name, body), sym) def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative = - untpd.Alternative(trees).withType(ctx.typeComparer.lub(trees map (_.tpe))).checked + ta.assignType(untpd.Alternative(trees), trees) - def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree])(implicit ctx: Context): UnApply = { - val owntype = fun.tpe.widen match { - case MethodType(_, paramType :: Nil) => paramType - case _ => check(false); ErrorType - } - untpd.UnApply(fun, implicits, patterns).withType(owntype).checked - } + def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(implicit ctx: Context): UnApply = + ta.assignType(untpd.UnApply(fun, implicits, patterns), proto) def ValDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): ValDef = - untpd.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs).withType(sym.valRef).checked + ta.assignType(untpd.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs), sym) def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = - DefDef(sym, Function.const(rhs) _) + ta.assignType(DefDef(sym, Function.const(rhs) _), sym) def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { - val (tparams, mtp) = sym.info match { case tp: PolyType => val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds) @@ -271,15 +187,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } val (vparamss, rtp) = valueParamss(mtp) val argss = vparamss map (_ map (vparam => Ident(vparam.termRef))) - untpd.DefDef( - Modifiers(sym), sym.name, tparams map TypeDef, - vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhsFn(argss)) - .withType(sym.termRefWithSig).checked + ta.assignType( + untpd.DefDef( + Modifiers(sym), sym.name, tparams map TypeDef, + vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhsFn(argss)), sym) } def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef = - untpd.TypeDef(Modifiers(sym), sym.name, TypeTree(sym.info)) - .withType(sym.typeRef).checked + ta.assignType(untpd.TypeDef(Modifiers(sym), sym.name, TypeTree(sym.info)), sym) def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree])(implicit ctx: Context): TypeDef = { val parents = cls.info.parents map (TypeTree(_)) @@ -297,18 +212,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { .orElse(ctx.newLocalDummy(cls)) val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body) .withType(localDummy.termRef).checked - untpd.TypeDef(Modifiers(cls), cls.name, impl) - .withType(cls.typeRef).checked + ta.assignType(untpd.TypeDef(Modifiers(cls), cls.name, impl), cls) } def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = - untpd.Import(expr, selectors).withType(ctx.newImportSymbol(expr).termRef).checked + ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(expr)) def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = - untpd.PackageDef(pid, stats).withType(pid.symbol.namedType).checked + ta.assignType(untpd.PackageDef(pid, stats), pid) def Annotated(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated = - untpd.Annotated(annot, arg).withType(AnnotatedType(Annotation(annot), arg.tpe)).checked + ta.assignType(untpd.Annotated(annot, arg), annot, arg) // ------ Making references ------------------------------------------------------ @@ -323,14 +237,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ref(sym: Symbol)(implicit ctx: Context): tpd.NameTree = ref(NamedType(sym.owner.thisType, sym.name, sym.denot)) - // ----- Converting to releated trees ----------------------------------------------- - - def underlyingIfRepeated(id: Ident)(implicit ctx: Context): Ident = - if (id.isType) id else id withType id.tpe.underlyingIfRepeated - - def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = - Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) - // ------ Creating typed equivalents of trees that exist only in untyped form ------- /** new C(args) */ @@ -453,7 +359,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = { - val locals = localSyms(trees) + val locals = ta.localSyms(trees) val mapped = ctx.mapSymbols(locals, typeMap, ownerMap) if (locals eq mapped) super.transform(trees) else withSubstitution(locals, mapped).transform(trees) @@ -476,7 +382,5 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { // ensure that constructors are fully applied? // ensure that normal methods are fully applied? - def localSyms(stats: List[tpd.Tree])(implicit ctx: Context): List[Symbol] = - for (stat <- stats if stat.isDef) yield stat.symbol } diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index c69f60758..622752570 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -988,7 +988,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: case UNAPPLYtree => val fun = readTreeRef() val args = until(end, readTreeRef) - UnApply(fun, Nil, args) // !!! this is wrong in general + UnApply(fun, Nil, args, defn.AnyType) // !!! this is wrong in general case ARRAYVALUEtree => val elemtpt = readTreeRef() @@ -1067,7 +1067,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: setSym() val qual = readTreeRef() val mix = readTypeNameRef() - Super(qual, mix) + Super(qual, mix, inConstrCall = false) // todo: revise case THIStree => setSym() diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 26325e57e..cf0d7d0a1 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -398,8 +398,9 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val ownType = - if (!success) ErrorType + val app0 = cpy.Apply(app, normalizedFun, typedArgs) + val app1 = + if (!success) app0.withType(ErrorType) else { if (!sameSeq(args, orderedArgs)) { // need to lift arguments to maintain evaluation order in the @@ -411,9 +412,9 @@ trait Applications extends Compatibility { self: Typer => } if (sameSeq(typedArgs, args)) // trick to cut down on tree copying typedArgs = args.asInstanceOf[List[Tree]] - methodType.instantiate(typedArgs.tpes) + assignType(app0, normalizedFun, typedArgs) } - wrapDefs(liftedDefs, cpy.Apply(app, normalizedFun, typedArgs).withType(ownType)) + wrapDefs(liftedDefs, app1) } } @@ -513,21 +514,11 @@ trait Applications extends Compatibility { self: Typer => def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") { val typedArgs = tree.args mapconserve (typedType(_)) val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt)) - val ownType = typedFn.tpe.widen match { - case pt: PolyType => - checkBounds(typedArgs, pt, tree.pos) - val argTypes = typedArgs.tpes - if (argTypes.length == pt.paramNames.length) - pt.resultType.substParams(pt, typedArgs.tpes) - else { - ctx.error(i"wrong number of type parameters for ${typedFn.tpe}; expected: ${pt.paramNames.length}", tree.pos) - ErrorType - } + typedFn.tpe.widen match { + case pt: PolyType => checkBounds(typedArgs, pt, tree.pos) case _ => - ctx.error(s"${err.exprStr(typedFn)} does not take type parameters", tree.pos) - ErrorType } - cpy.TypeApply(tree, typedFn, typedArgs).withType(ownType) + assignType(cpy.TypeApply(tree, typedFn, typedArgs), typedFn, typedArgs) } def typedUnApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = track("typedUnApply") { @@ -577,13 +568,13 @@ trait Applications extends Compatibility { self: Typer => /** Produce a typed qual.unappy or qual.unappySeq tree, or * else if this fails follow a type alias and try again. */ - val unapply = trySelectUnapply(qual) { sel => + val unapplyFn = trySelectUnapply(qual) { sel => val qual1 = followTypeAlias(qual) if (qual1.isEmpty) notAnExtractor(sel) else trySelectUnapply(qual1)(_ => notAnExtractor(sel)) } - def fromScala2x = unapply.symbol.exists && (unapply.symbol.owner is Scala2x) + def fromScala2x = unapplyFn.symbol.exists && (unapplyFn.symbol.owner is Scala2x) def unapplyArgs(unapplyResult: Type)(implicit ctx: Context): List[Type] = { def extractorMemberType(tp: Type, name: Name) = { @@ -609,7 +600,7 @@ trait Applications extends Compatibility { self: Typer => // println(s"unapply $unapplyResult ${extractorMemberType(unapplyResult, nme.isDefined)}") if (extractorMemberType(unapplyResult, nme.isDefined) isRef defn.BooleanClass) { if (getTp.exists) - if (unapply.symbol.name == nme.unapplySeq) { + if (unapplyFn.symbol.name == nme.unapplySeq) { val seqArg = boundsToHi(getTp.firstBaseArgInfo(defn.SeqClass)) if (seqArg.exists) return args map Function.const(seqArg) } @@ -634,7 +625,7 @@ trait Applications extends Compatibility { self: Typer => case _ => false } - unapply.tpe.widen match { + unapplyFn.tpe.widen match { case mt: MethodType if mt.paramTypes.length == 1 && !mt.isDependent => val unapplyArgType = mt.paramTypes.head unapp.println(s"unapp arg tpe = ${unapplyArgType.show}, pt = ${pt.show}") @@ -661,7 +652,7 @@ trait Applications extends Compatibility { self: Typer => // can open unsoundness holes. See SI-7952 for an example of the hole this opens. if (ctx.settings.verbose.value) ctx.warning(msg, tree.pos) } else { - unapp.println(s" ${unapply.symbol.owner} ${unapply.symbol.owner is Scala2x}") + unapp.println(s" ${unapplyFn.symbol.owner} ${unapplyFn.symbol.owner is Scala2x}") ctx.error(msg, tree.pos) } case _ => @@ -677,7 +668,7 @@ trait Applications extends Compatibility { self: Typer => } val dummyArg = dummyTreeOfType(unapplyArgType) - val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapply, dummyArg :: Nil))) + val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) val unapplyImplicits = unapplyApp match { case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); args2 case Apply(unapply, `dummyArg` :: Nil) => Nil @@ -695,12 +686,12 @@ trait Applications extends Compatibility { self: Typer => List.fill(argTypes.length - args.length)(WildcardType) } val unapplyPatterns = (bunchedArgs, argTypes).zipped map (typed(_, _)) - val result = cpy.UnApply(tree, unapply, unapplyImplicits, unapplyPatterns) withType ownType + val result = assignType(cpy.UnApply(tree, unapplyFn, unapplyImplicits, unapplyPatterns), ownType) unapp.println(s"unapply patterns = $unapplyPatterns") if ((ownType eq pt) || ownType.isError) result else Typed(result, TypeTree(ownType)) case tp => - val unapplyErr = if (tp.isError) unapply else notAnExtractor(unapply) + val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) cpy.UnApply(tree, unapplyErr, Nil, typedArgsErr) withType ErrorType } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 5c21584bd..25e6a7aa7 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -5,7 +5,7 @@ package typer import core._ import ast._ import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ -import Trees._ +import Trees._, ProtoTypes._ import Constants._ import Scopes._ import annotation.unchecked @@ -20,6 +20,7 @@ import collection.mutable trait NoChecking { import tpd._ + def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = () def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp @@ -33,6 +34,16 @@ trait Checking extends NoChecking { import tpd._ + /** Check that Java statics and packages can only be used in selections. + */ + override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { + if (!proto.isInstanceOf[SelectionProto]) { + val sym = tree.tpe.termSymbol + if ((sym is Package) || (sym is JavaModule)) ctx.error(i"$sym is not a value", tree.pos) + } + tree + } + /** Check that type arguments `args` conform to corresponding bounds in `poly` */ override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = for ((arg, bounds) <- args zip poly.paramBounds) { diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 6e91e4754..c24021936 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -591,7 +591,7 @@ class Namer { typer: Typer => (paramSymss.isEmpty || paramSymss.head.nonEmpty && (paramSymss.head.head is Implicit))) paramSymss = Nil :: paramSymss val restpe1 = // try to make anonymous functions non-dependent, so that they can be used in closures - if (name == nme.ANON_FUN) tpd.avoid(restpe, paramSymss.flatten) + if (name == nme.ANON_FUN) avoid(restpe, paramSymss.flatten) else restpe val monotpe = (paramSymss :\ restpe1) { (params, restpe) => diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index e711ee949..6eeec56e1 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -4,15 +4,182 @@ package typer import core._ import ast._ -import Scopes._, Contexts._, Constants._, Types._, Symbols._ +import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ +import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._ +import util.Positions._ +import config.Printers._ -trait TypeAssigner extends NoChecking { +trait TypeAssigner { + import tpd._ - def assignType(tree: untpd.New)(implicit ctx: Context) = { - checkClassTypeWithStablePrefix(tree.tpt.tpe, tree.tpt.pos, traitReq = false) - tree.withType(tree.tpt.tpe) + /** The enclosing class, except if we are in a super call, in which case + * it is the next outer one. + */ + def effectiveEnclosingClass(implicit ctx: Context) = { + val enclClass = ctx.owner.enclosingClass + if ((ctx.mode is Mode.InSuperCall) && enclClass.exists) enclClass.owner.enclosingClass + else enclClass } + /** The qualifying class of a this or super with prefix `qual` (which might be empty). + * @param packageOk The qualifier may refer to a package. + */ + def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol = { + effectiveEnclosingClass.ownersIterator.find(o => qual.isEmpty || o.isClass && o.name == qual) match { + case Some(c) if packageOK || !(c is Package) => + c + case _ => + ctx.error( + if (qual.isEmpty) tree.show + " can be used only in a class, object, or template" + else qual.show + " is not an enclosing class", tree.pos) + NoSymbol + } + } + + def avoid(tp: Type, syms: => List[Symbol])(implicit ctx: Context): Type = { + val widenMap = new TypeMap { + lazy val forbidden = syms.toSet + def toAvoid(tp: Type): Boolean = tp match { + case tp: TermRef => + val sym = tp.symbol + sym.exists && ( + sym.owner.isTerm && (forbidden contains sym) + || !(sym.owner is Package) && toAvoid(tp.prefix) + ) + case _ => + false + } + def apply(tp: Type) = tp match { + case tp: TermRef if toAvoid(tp) && variance > 0 => + apply(tp.info) + case tp: TypeRef if toAvoid(tp.prefix) => + tp.info match { + case TypeAlias(ref) => apply(ref) + case _ => mapOver(tp) + } + case tp: RefinedType => + val tp1 @ RefinedType(parent1, _) = mapOver(tp) + if (tp1.refinedInfo existsPart toAvoid) { + typr.println(s"dropping refinement from $tp1") + parent1 + } + else tp1 + case _ => + mapOver(tp) + } + } + widenMap(tp) + } + + def localSyms(stats: List[tpd.Tree])(implicit ctx: Context): List[Symbol] = + for (stat <- stats if stat.isDef) yield stat.symbol + + def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = + Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) + + /** A denotation exists really if it exists and does not point to a stale symbol. */ + final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try + denot match { + case denot: SymDenotation => + denot.exists && { + denot.ensureCompleted + !denot.isAbsent + } + case denot: SingleDenotation => + val sym = denot.symbol + (sym eq NoSymbol) || reallyExists(sym.denot) + case _ => + true + } + catch { + case ex: StaleSymbol => false + } + + /** If `tpe` is a named type, check that its denotation is accessible in the + * current context. Return the type with those alternatives as denotations + * which are accessible. + */ + def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { + def test(tpe: Type, firstTry: Boolean): Type = tpe match { + case tpe: NamedType => + val pre = tpe.prefix + val name = tpe.name + val d = tpe.denot.accessibleFrom(pre, superAccess) + if (!d.exists) { + // it could be that we found an inaccessbile private member, but there is + // an inherited non-private member with the same name and signature. + val d2 = pre.nonPrivateMember(name) + if (reallyExists(d2) && firstTry) test(pre.select(name, d2), false) + else { + val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) + val what = alts match { + case Nil => + name.toString + case sym :: Nil => + if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated + case _ => + i"none of the overloaded alternatives named $name" + } + val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" + val whyNot = new StringBuffer + alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) + if (!tpe.isError) + ctx.error(i"$what cannot be accessed as a member of $pre$where.$whyNot", pos) + ErrorType + } + } else if (d.symbol is TypeParamAccessor) // always dereference type param accessors + ensureAccessible(d.info.bounds.hi, superAccess, pos) + else + tpe withDenot d + case _ => + tpe + } + test(tpe, true) + } + + /** The type of a selection with `name` of a tree with type `site`. + */ + def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = { + val mbr = site.member(name) + if (reallyExists(mbr)) site.select(name, mbr) + else { + if (!site.isErroneous) { + ctx.error( + if (name == nme.CONSTRUCTOR) i"$site does not have a constructor" + else i"$name is not a member of $site", pos) + } + ErrorType + } + } + + /** The selection type, which is additionally checked for accessibility. + */ + def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = { + val ownType = selectionType(qual1.tpe.widenIfUnstable, tree.name, tree.pos) + ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.pos) + } + + /** Type assignment method. Each method takes as parameters + * - an untpd.Tree to which it assigns a type, + * - typed child trees it needs to access to cpmpute that type, + * - any further information it needs to access to compute that type. + */ + + def assignType(tree: untpd.Ident, rawType: Type)(implicit ctx: Context) = { + tree.withType(if (tree.isType) rawType else rawType.underlyingIfRepeated) + } + + def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context) = { + tree.withType(accessibleSelectionType(tree, qual)) + } + + def assignType(tree: untpd.SelectFromTypeTree, qual: Tree)(implicit ctx: Context) = { + tree.withType(accessibleSelectionType(tree, qual)) + } + + def assignType(tree: untpd.New, tpt: Tree)(implicit ctx: Context) = + tree.withType(tpt.tpe) + def assignType(tree: untpd.Literal)(implicit ctx: Context) = tree.withType { tree.const.tag match { @@ -21,6 +188,146 @@ trait TypeAssigner extends NoChecking { case _ => ConstantType(tree.const) } } + + def assignType(tree: untpd.This)(implicit ctx: Context) = { + val cls = qualifyingClass(tree, tree.qual, packageOK = false) + tree.withType(cls.thisType) + } + + def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean)(implicit ctx: Context) = { + val mix = tree.mix + val cls = qual.tpe.widen.typeSymbol + + def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { + case p :: Nil => + p + case Nil => + errorType(i"$mix does not name a parent class of $cls", tree.pos) + case p :: q :: _ => + errorType(s"ambiguous parent class qualifier", tree.pos) + } + val owntype = + if (!mix.isEmpty) findMixinSuper(cls.info) + else if (inConstrCall) cls.info.firstParent + else cls.info.parents.reduceLeft((x: Type, y: Type) => AndType(x, y)) + tree.withType(SuperType(cls.thisType, owntype)) + } + + def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { + val ownType = fn.tpe.widen match { + case fntpe @ MethodType(_, ptypes) => + if (sameLength(ptypes, args)) fntpe.instantiate(args.tpes) + else errorType(s"wrong number of type parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos) + case t => + errorType(s"${err.exprStr(fn)} does not take type parameters", tree.pos) + } + tree.withType(ownType) + } + + def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { + val ownType = fn.tpe.widen match { + case pt: PolyType => + val argTypes = args.tpes + if (sameLength(argTypes, pt.paramNames)) pt.instantiate(args.tpes) + else errorType(i"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos) + case _ => + errorType(s"${err.exprStr(fn)} does not take type parameters", tree.pos) + } + tree.withType(ownType) + } + + def assignType(tree: untpd.Pair, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(defn.PairType.appliedTo(left.tpe :: right.tpe :: Nil)) + + def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context) = + tree.withType(tpt.tpe) + + def assignType(tree: untpd.NamedArg, arg: Tree)(implicit ctx: Context) = + tree.withType(arg.tpe) + + def assignType(tree: untpd.Assign)(implicit ctx: Context) = + tree.withType(defn.UnitType) + + def assignType(tree: untpd.Block, stats: List[Tree], expr: Tree)(implicit ctx: Context) = + tree.withType(avoid(expr.tpe, localSyms(stats))) + + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = + tree.withType(thenp.tpe | elsep.tpe) + + def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = + tree.withType(if (target.isEmpty) meth.tpe.widen.toFunctionType else target.tpe) + + def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = + tree.withType(body.tpe) + + def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = + tree.withType(ctx.typeComparer.lub(cases.tpes)) + + def assignType(tree: untpd.Return)(implicit ctx: Context) = + tree.withType(defn.NothingType) + + def assignType(tree: untpd.Try, expr: Tree, handler: Tree)(implicit ctx: Context) = { + val handlerTypeArgs = handler.tpe.baseArgTypesHi(defn.FunctionClass(1)) + tree.withType(if (handlerTypeArgs.nonEmpty) expr.tpe | handlerTypeArgs(1) else expr.tpe) + } + + def assignType(tree: untpd.Throw)(implicit ctx: Context) = + tree.withType(defn.NothingType) + + def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = + tree.withType(defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes))) + + def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = + tree.withType(ref.tpe) + + def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(left.tpe & right.tpe) + + def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(left.tpe | right.tpe) + + // RefinedTypeTree is missing, handled specially in Typer and Unpickler. + + def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { + val tparams = tycon.tpe.typeParams + val ownType = + if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) + else errorType(i"wrong number of type arguments for ${tycon.tpe}, should be ${tparams.length}", tree.pos) + tree.withType(ownType) + } + + def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = + tree.withType(ExprType(result.tpe)) + + def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = + tree.withType(TypeBounds(lo.tpe, hi.tpe)) + + def assignType(tree: untpd.Bind, sym: TermSymbol)(implicit ctx: Context) = + tree.withType(TermRef(NoPrefix, sym)) + + def assignType(tree: untpd.Alternative, trees: List[Tree])(implicit ctx: Context) = + tree.withType(ctx.typeComparer.lub(trees.tpes)) + + def assignType(tree: untpd.UnApply, proto: Type)(implicit ctx: Context) = + tree.withType(proto) + + def assignType(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(if (sym.exists) sym.valRef else NoType) + + def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(sym.termRefWithSig) + + def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(sym.typeRef) + + def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) = + tree.withType(sym.termRef) + + def assignType(tree: untpd.Annotated, annot: Tree, arg: Tree)(implicit ctx: Context) = + tree.withType(AnnotatedType(Annotation(annot), arg.tpe)) + + def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = + tree.withType(pid.symbol.valRef) } object TypeAssigner extends TypeAssigner diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index c545641a0..51eba3b02 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -62,120 +62,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ private var importedFromRoot: Set[Symbol] = Set() - /** A denotation exists really if it exists and does not point to a stale symbol. */ - final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try - denot match { - case denot: SymDenotation => - denot.exists && { - denot.ensureCompleted - !denot.isAbsent - } - case denot: SingleDenotation => - val sym = denot.symbol - (sym eq NoSymbol) || reallyExists(sym.denot) - case _ => - true - } - catch { - case ex: StaleSymbol => false - } - - /** The type of a selection with `name` of a tree with type `site`. - */ - def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = { - val mbr = site.member(name) - if (reallyExists(mbr)) site.select(name, mbr) - else { - if (!site.isErroneous) { - ctx.error( - if (name == nme.CONSTRUCTOR) i"$site does not have a constructor" - else i"$name is not a member of $site", pos) - } - ErrorType - } - } - - /** The selection type, which is additionally checked for accessibility. - */ - def checkedSelectionType(qual1: Tree, tree: untpd.RefTree)(implicit ctx: Context): Type = { - val ownType = selectionType(qual1.tpe.widenIfUnstable, tree.name, tree.pos) - checkAccessible(ownType, qual1.isInstanceOf[Super], tree.pos) - } - - /** Check that Java statics and packages can only be used in selections. - */ - def checkValue(tpe: Type, proto: Type, pos: Position)(implicit ctx: Context): Unit = - if (!proto.isInstanceOf[SelectionProto]) { - val sym = tpe.termSymbol - if ((sym is Package) || (sym is JavaModule)) ctx.error(i"$sym is not a value", pos) - } - - /** If `tpe` is a named type, check that its denotation is accessible in the - * current context. Return the type with those alternatives as denotations - * which are accessible. - */ - def checkAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { - def test(tpe: Type, firstTry: Boolean): Type = tpe match { - case tpe: NamedType => - val pre = tpe.prefix - val name = tpe.name - val d = tpe.denot.accessibleFrom(pre, superAccess) - if (!d.exists) { - // it could be that we found an inaccessbile private member, but there is - // an inherited non-private member with the same name and signature. - val d2 = pre.nonPrivateMember(name) - if (reallyExists(d2) && firstTry) test(pre.select(name, d2), false) - else { - val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) - val what = alts match { - case Nil => - name.toString - case sym :: Nil => - if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated - case _ => - i"none of the overloaded alternatives named $name" - } - val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" - val whyNot = new StringBuffer - alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) - if (!tpe.isError) - ctx.error(i"$what cannot be accessed as a member of $pre$where.$whyNot", pos) - ErrorType - } - } else if (d.symbol is TypeParamAccessor) // always dereference type param accessors - checkAccessible(d.info.bounds.hi, superAccess, pos) - else - tpe withDenot d - case _ => - tpe - } - test(tpe, true) - } - - /** The enclosing class, except if we are in a super call, in which case - * it is the next outer one. - */ - def effectiveEnclosingClass(implicit ctx: Context) = { - val enclClass = ctx.owner.enclosingClass - if ((ctx.mode is Mode.InSuperCall) && enclClass.exists) enclClass.owner.enclosingClass - else enclClass - } - - /** The qualifying class of a this or super with prefix `qual` (which might be empty). - * @param packageOk The qualifier may refer to a package. - */ - def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol = { - effectiveEnclosingClass.ownersIterator.find(o => qual.isEmpty || o.isClass && o.name == qual) match { - case Some(c) if packageOK || !(c is Package) => - c - case _ => - ctx.error( - if (qual.isEmpty) tree.show + " can be used only in a class, object, or template" - else qual.show + " is not an enclosing class", tree.pos) - NoSymbol - } - } - /** Attribute an identifier consisting of a simple name or wildcard * * @param tree The tree representing the identifier. @@ -366,50 +252,38 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val rawType = try findRef(NoType, BindingPrec.nothingBound, NoContext) finally importedFromRoot = saved - checkValue(rawType, pt, tree.pos) val ownType = if (rawType.exists) - checkAccessible(rawType, superAccess = false, tree.pos) + ensureAccessible(rawType, superAccess = false, tree.pos) else { error(i"not found: $kind$name", tree.pos) ErrorType } - tree.withType(ownType.underlyingIfRepeated) + checkValue(tree.withType(ownType.underlyingIfRepeated), pt) } def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) - val ownType = checkedSelectionType(qual1, tree) - checkValue(ownType, pt, tree.pos) - cpy.Select(tree, qual1, tree.name).withType(ownType) + checkValue(assignType(cpy.Select(tree, qual1, tree.name), qual1), pt) + } + + def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = track("typedSelectFromTypeTree") { + val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) + assignType(cpy.SelectFromTypeTree(tree, qual1, tree.name), qual1) } def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = track("typedThis") { - val cls = qualifyingClass(tree, tree.qual, packageOK = false) - tree.withType(cls.thisType) + assignType(tree) } def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = track("typedSuper") { - val mix = tree.mix val qual1 = typed(tree.qual) - val cls = qual1.tpe.widen.typeSymbol - - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { - case p :: Nil => - p - case Nil => - errorType(i"$mix does not name a parent class of $cls", tree.pos) - case p :: q :: _ => - errorType(s"ambiguous parent class qualifier", tree.pos) + val inConstrCall = pt match { + case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true + case _ => false } - val owntype = - if (!mix.isEmpty) findMixinSuper(cls.info) - else pt match { - case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => cls.info.firstParent - case _ => cls.info.parents.reduceLeft((x: Type, y: Type) => AndType(x, y)) - } - cpy.Super(tree, qual1, mix).withType(SuperType(cls.thisType, owntype)) + assignType(cpy.Super(tree, qual1, tree.mix), qual1, inConstrCall) } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { @@ -425,7 +299,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => val tpt1 = typedType(tree.tpt) - assignType(cpy.New(tree, tpt1)) + checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + assignType(cpy.New(tree, tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } } @@ -437,7 +312,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val left1 = typed(tree.left, leftProto) val right1 = typed(tree.right, rightProto) - cpy.Pair(tree, left1, right1).withType(defn.PairType.appliedTo(left1.tpe :: right1.tpe :: Nil)) + assignType(cpy.Pair(tree, left1, right1), left1, right1) } def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") { @@ -446,7 +321,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val expr1 = if (isWildcard) tree.expr withType tpt1.tpe else typedExpr(tree.expr, tpt1.tpe) - cpy.Typed(tree, expr1, tpt1).withType(tpt1.tpe) + assignType(cpy.Typed(tree, expr1, tpt1), tpt1) } tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => @@ -465,7 +340,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") { val arg1 = typed(tree.arg, pt) - cpy.NamedArg(tree, tree.name, arg1).withType(arg1.tpe) + assignType(cpy.NamedArg(tree, tree.name, arg1), arg1) } def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = track("typedAssign") { @@ -481,7 +356,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def lhs1 = typed(untpd.TypedSplice(lhsCore)) lhsCore.tpe match { case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) => - cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info)).withType(defn.UnitType) + assignType(cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info))) case _ => def reassignmentToVal = errorTree(cpy.Assign(tree, lhsCore, typed(tree.rhs, lhs1.tpe.widen)), @@ -494,7 +369,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit lhsCore match { case lhsCore: RefTree if setter.exists => val setterTypeRaw = pre select (setterName, setter) - val setterType = checkAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) + val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) val lhs2 = lhsCore.withName(setterName).withType(setterType) typed(cpy.Apply(tree, untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => @@ -511,8 +386,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val exprCtx = index(tree.stats) val stats1 = typedStats(tree.stats, ctx.owner) val expr1 = typedExpr(tree.expr, pt)(exprCtx) - val result = cpy.Block(tree, stats1, expr1).withType(avoid(expr1.tpe, localSyms(stats1))) - checkNoLocalRefs(result, pt) + ensureNoLocalRefs(assignType(cpy.Block(tree, stats1, expr1), stats1, expr1), pt) } /** Check that block's type can be expressed without references to locally defined @@ -522,17 +396,18 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * 2. If (1) fails, force all type variables so that the block's type is * fully defined and try again. */ - def checkNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = { + private def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = { val Block(stats, expr) = block val leaks = CheckTrees.escapingRefs(block) if (leaks.isEmpty) block else if (isFullyDefined(pt, ForceDegree.all)) { - val expr1 = typed(untpd.Typed(untpd.TypedSplice(expr), untpd.TypeTree(pt))) - untpd.Block(stats, expr1) withType expr1.tpe + val expr1 = Typed(expr, TypeTree(pt)) + cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant } else if (!forcedDefined) { fullyDefinedType(block.tpe, "block", block.pos) - val block1 = block.withType(avoid(block.tpe, localSyms(stats))) - checkNoLocalRefs(block1, pt, forcedDefined = true) + val expr1 = Typed(expr, TypeTree(avoid(block.tpe, localSyms(stats)))) + val block1 = cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is already done + ensureNoLocalRefs(block1, pt, forcedDefined = true) } else errorTree(block, i"local definition of ${leaks.head.name} escapes as part of block's type ${block.tpe}"/*; full type: ${result.tpe.toString}"*/) @@ -542,7 +417,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val cond1 = typed(tree.cond, defn.BooleanType) val thenp1 = typed(tree.thenp, pt) val elsep1 = typed(tree.elsep orElse untpd.unitLiteral withPos tree.pos, pt) - cpy.If(tree, cond1, thenp1, elsep1).withType(thenp1.tpe | elsep1.tpe) + assignType(cpy.If(tree, cond1, thenp1, elsep1), thenp1, elsep1) } def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { @@ -649,21 +524,21 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = track("typedClosure") { val env1 = tree.env mapconserve (typed(_)) val meth1 = typedUnadapted(tree.meth) - val (ownType, target) = meth1.tpe.widen match { + val target = meth1.tpe.widen match { case mt: MethodType => pt match { case SAMType(meth) if !defn.isFunctionType(pt) && mt <:< meth.info => if (!isFullyDefined(pt, ForceDegree.all)) ctx.error(i"result type of closure is an underspecified SAM type $pt", tree.pos) - (pt, TypeTree(pt)) + TypeTree(pt) case _ => - if (!mt.isDependent) (mt.toFunctionType, EmptyTree) + if (!mt.isDependent) EmptyTree else throw new Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error? } case tp => throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}") } - cpy.Closure(tree, env1, meth1, target).withType(ownType) + assignType(cpy.Closure(tree, env1, meth1, target), meth1, target) } def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { @@ -706,7 +581,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val guard1 = typedExpr(tree.guard, defn.BooleanType) val body1 = typedExpr(tree.body, pt) - cpy.CaseDef(tree, pat, guard1, body1) withType body1.tpe + assignType(cpy.CaseDef(tree, pat, guard1, body1), body1) } val doCase: () => CaseDef = () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.withNewScope) @@ -714,7 +589,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val cases1 = tree.cases mapconserve typedCase - cpy.Match(tree, sel1, cases1).withType(ctx.typeComparer.lub(cases1.tpes)) + assignType(cpy.Match(tree, sel1, cases1), cases1) } } @@ -736,27 +611,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val (from, proto) = enclMethInfo(ctx) val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withPos(tree.pos), proto) - cpy.Return(tree, expr1, from) withType defn.NothingType + assignType(cpy.Return(tree, expr1, from)) } def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") { val expr1 = typed(tree.expr, pt) val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt)) val finalizer1 = typed(tree.finalizer, defn.UnitType) - val handlerTypeArgs = handler1.tpe.baseArgTypesHi(defn.FunctionClass(1)) - val ownType = if (handlerTypeArgs.nonEmpty) expr1.tpe | handlerTypeArgs(1) else expr1.tpe - cpy.Try(tree, expr1, handler1, finalizer1) withType ownType + assignType(cpy.Try(tree, expr1, handler1, finalizer1), expr1, handler1) } def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Throw = track("typedThrow") { val expr1 = typed(tree.expr, defn.ThrowableType) - cpy.Throw(tree, expr1) withType defn.NothingType + assignType(cpy.Throw(tree, expr1)) } def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") { val proto1 = pt.elemType orElse WildcardType val elems1 = tree.elems mapconserve (typed(_, proto1)) - cpy.SeqLiteral(tree, elems1) withType defn.SeqType.appliedTo(ctx.typeComparer.lub(elems1.tpes)) + assignType(cpy.SeqLiteral(tree, elems1), elems1) } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { @@ -770,24 +643,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) checkStable(ref1.tpe, tree.pos) - cpy.SingletonTypeTree(tree, ref1) withType ref1.tpe - } - - def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = track("typedSelectFromTypeTree") { - val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) - cpy.SelectFromTypeTree(tree, qual1, tree.name).withType(checkedSelectionType(qual1, tree)) + assignType(cpy.SingletonTypeTree(tree, ref1), ref1) } def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - cpy.AndTypeTree(tree, left1, right1) withType left1.tpe & right1.tpe + assignType(cpy.AndTypeTree(tree, left1, right1), left1, right1) } def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - cpy.OrTypeTree(tree, left1, right1) withType left1.tpe | right1.tpe + assignType(cpy.OrTypeTree(tree, left1, right1), left1, right1) } def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") { @@ -818,16 +686,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): AppliedTypeTree = track("typedAppliedTypeTree") { val tpt1 = typed(tree.tpt) val args1 = tree.args mapconserve (typed(_)) - val tparams = tpt1.tpe.typeParams - if (args1.length != tparams.length) - ctx.error(i"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}") // todo in later phase: check arguments conform to parameter bounds - cpy.AppliedTypeTree(tree, tpt1, args1) withType tpt1.tpe.appliedTo(args1.tpes) + assignType(cpy.AppliedTypeTree(tree, tpt1, args1), tpt1, args1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { val result1 = typed(tree.result) - cpy.ByNameTypeTree(tree, result1) withType ExprType(result1.tpe) + assignType(cpy.ByNameTypeTree(tree, result1), result1) } def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { @@ -836,19 +701,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val hi1 = typed(hi) if (!(lo1.tpe <:< hi1.tpe)) ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) - cpy.TypeBoundsTree(tree, lo1, hi1) withType TypeBounds(lo1.tpe, hi1.tpe) + assignType(cpy.TypeBoundsTree(tree, lo1, hi1), lo1, hi1) } def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = track("typedBind") { val body1 = typed(tree.body, pt) typr.println(s"typed bind ${tree.show} pt = ${pt.show} bodytpe = ${body1.tpe.show}") val sym = ctx.newSymbol(ctx.owner, tree.name.asTermName, EmptyFlags, body1.tpe, coord = tree.pos) - cpy.Bind(tree, tree.name, body1) withType TermRef(NoPrefix, sym) + assignType(cpy.Bind(tree, tree.name, body1), sym) } def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") { val trees1 = tree.trees mapconserve (typed(_, pt)) - cpy.Alternative(tree, trees1) withType ctx.typeComparer.lub(trees1.tpes) + assignType(cpy.Alternative(tree, trees1), trees1) } def typedModifiers(mods: untpd.Modifiers)(implicit ctx: Context): Modifiers = track("typedModifiers") { @@ -870,8 +735,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Ident(nme.WILDCARD) => rhs withType tpt1.tpe case _ => typedExpr(rhs, tpt1.tpe) } - val refType = if (sym.exists) sym.valRef else NoType - cpy.ValDef(vdef, mods1, name, tpt1, rhs1).withType(refType) + assignType(cpy.ValDef(vdef, mods1, name, tpt1, rhs1), sym) } def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { @@ -885,7 +749,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val tpt1 = typedType(tpt) val rhs1 = typedExpr(rhs, tpt1.tpe) - cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1).withType(sym.termRefWithSig) + assignType(cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) //todo: make sure dependent method types do not depend on implicits or by-name params } @@ -893,7 +757,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeDef(mods, name, rhs) = tdef val mods1 = typedModifiers(mods) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree - cpy.TypeDef(tdef, mods1, name, TypeTree(sym.info)).withType(sym.typeRef) + assignType(cpy.TypeDef(tdef, mods1, name, TypeTree(sym.info)), sym) } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { @@ -928,7 +792,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit checkNoDoubleDefs(cls) val impl1 = cpy.Template(impl, constr1, parents1, self1, body1) .withType(localDummy.termRef) - cpy.TypeDef(cdef, mods1, name, impl1).withType(cls.typeRef) + assignType(cpy.TypeDef(cdef, mods1, name, impl1), cls) // todo later: check that // 1. If class is non-abstract, it is instantiatable: @@ -942,23 +806,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.pos) - cpy.Import(imp, expr1, imp.selectors).withType(sym.termRef) - } - - def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { - val annot1 = typed(tree.annot, defn.AnnotationClass.typeRef) - val arg1 = typed(tree.arg, pt) - val underlyingType = if (arg1.isTerm) arg1.tpe.widen else arg1.tpe - val ownType = AnnotatedType(Annotation(annot1), underlyingType) - if (ctx.mode is Mode.Type) - cpy.Annotated(tree, annot1, arg1) withType ownType - else - cpy.Typed(tree, arg1, TypeTree(ownType)) withType ownType + assignType(cpy.Import(imp, expr1, imp.selectors), sym) } - def typedAsFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = - typed(tree, if (defn.isFunctionType(pt)) pt else AnyFunctionProto) - def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { val pid1 = typedExpr(tree.pid, AnySelectionProto) val pkg = pid1.symbol @@ -972,6 +822,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cpy.PackageDef(tree, pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef } + def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { + val annot1 = typed(tree.annot, defn.AnnotationClass.typeRef) + val arg1 = typed(tree.arg, pt) + if (ctx.mode is Mode.Type) + assignType(cpy.Annotated(tree, annot1, arg1), annot1, arg1) + else { + val tpt = TypeTree(AnnotatedType(Annotation(annot1), arg1.tpe.widen)) + assignType(cpy.Typed(tree, arg1, tpt), tpt) + } + } + + def typedAsFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = + typed(tree, if (defn.isFunctionType(pt)) pt else AnyFunctionProto) + def typedUnadapted(initTree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = { record("typedUnadapted") val xtree = expanded(initTree) -- cgit v1.2.3