diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala index 1fae0a1f10dd..832544706978 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/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 17e2c38173cb..e0e64f06a9c2 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/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 95f5b49be0bd..bda90b9e6518 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -11,133 +11,78 @@ 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, 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 = - untpd.Literal(const).withType(const.tpe).checked + 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 + 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 * @@ -157,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) @@ -196,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) @@ -269,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(_)) @@ -295,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 ------------------------------------------------------ @@ -321,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) */ @@ -451,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) @@ -474,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/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index dd96023d75c3..9be2e2f43dd9 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/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index d46a8387fdf0..da8263ac1a3f 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -715,10 +715,28 @@ 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 + 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 + */ + 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) /*<|<*/ { if (tp1 eq tp2) tp1 diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f9c8aad5cb68..bb30d9a9c7a6 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 diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index c69f60758518..6227525703f1 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/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2702143c1b52..5231ccd930b6 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 ce3198c643af..cf0d7d0a1b1a 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 @@ -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 new file mode 100644 index 000000000000..25e6a7aa7182 --- /dev/null +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -0,0 +1,141 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ +import Trees._, ProtoTypes._ +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 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 + 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 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) { + 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 + */ + 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. + */ + override 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 */ + 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) + case _ => + } + + /** Check that a non-implicit parameter making up the first parameter section of an + * implicit conversion is not a singleton type. + */ + 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) + 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. + */ + 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) => + 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 */ + override 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 0321906252a8..397285f6a3fa 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 008fe89669f2..b939c1b0d8f5 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 ea3109afac33..6d9afecabb20 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 463b7e71e256..c24021936d57 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 @@ -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) @@ -589,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/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala new file mode 100644 index 000000000000..b4068408b4ab --- /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 eq 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/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala new file mode 100644 index 000000000000..6eeec56e1232 --- /dev/null +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -0,0 +1,334 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ +import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._ +import util.Positions._ +import config.Printers._ + +trait TypeAssigner { + import tpd._ + + /** 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 { + case UnitTag => defn.UnitType + case NullTag => defn.NullType + 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 6cc3a226e3d5..51eba3b02564 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 TypeAssigner with Applications with Implicits with Inferencing with Checking { import Typer._ import tpd.{cpy => _, _} @@ -63,122 +62,6 @@ class Typer extends Namer with Applications with Implicits { */ 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) { - 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) - } - 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 - val addendum = - 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. @@ -245,7 +128,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)) { @@ -369,60 +252,42 @@ class Typer extends Namer with Applications with Implicits { 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") { - tree.withType { - tree.const.tag match { - case UnitTag => defn.UnitType - case NullTag => defn.NullType - case _ => ConstantType(tree.const) - } - } + assignType(tree) } def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") { @@ -433,10 +298,10 @@ 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) + checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + assignType(cpy.New(tree, tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) - cpy.New(tree, tpt1).withType(tpt1.tpe) } } @@ -447,7 +312,7 @@ class Typer extends Namer with Applications with Implicits { } 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") { @@ -456,7 +321,7 @@ class Typer extends Namer with Applications with Implicits { 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) => @@ -475,7 +340,7 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -491,7 +356,7 @@ class Typer extends Namer with Applications with Implicits { 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)), @@ -504,7 +369,7 @@ class Typer extends Namer with Applications with Implicits { 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 _ => @@ -521,8 +386,7 @@ class Typer extends Namer with Applications with Implicits { 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 @@ -532,17 +396,18 @@ class Typer extends Namer with Applications with Implicits { * 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}"*/) @@ -552,7 +417,7 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -659,21 +524,21 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -716,7 +581,7 @@ class Typer extends Namer with Applications with Implicits { } 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) @@ -724,7 +589,7 @@ class Typer extends Namer with Applications with Implicits { } val cases1 = tree.cases mapconserve typedCase - cpy.Match(tree, sel1, cases1).withType(ctx.typeComparer.lub(cases1.tpes)) + assignType(cpy.Match(tree, sel1, cases1), cases1) } } @@ -746,27 +611,25 @@ class Typer extends Namer with Applications with Implicits { } 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") { @@ -780,24 +643,19 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -828,16 +686,13 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -846,19 +701,19 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -880,8 +735,7 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -895,7 +749,7 @@ class Typer extends Namer with Applications with Implicits { } 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 } @@ -903,7 +757,7 @@ class Typer extends Namer with Applications with Implicits { 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") { @@ -938,7 +792,7 @@ class Typer extends Namer with Applications with Implicits { 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: @@ -952,23 +806,9 @@ class Typer extends Namer with Applications with Implicits { 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) + assignType(cpy.Import(imp, expr1, imp.selectors), sym) } - 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 - } - - 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 @@ -982,6 +822,20 @@ class Typer extends Namer with Applications with Implicits { 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) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 88c39231b97f..9914485ac3cb 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -41,6 +41,7 @@ class tests extends CompilerTest { @Test def pos_templateParents() = compileFile(posDir, "templateParents") @Test def pos_structural() = compileFile(posDir, "structural") @Test def pos_i39 = compileFile(posDir, "i39") + @Test def pos_overloadedAccess = compileFile(posDir, "overloadedAccess") @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) diff --git a/tests/pos/overloadedAccess.scala b/tests/pos/overloadedAccess.scala new file mode 100644 index 000000000000..a2d72f583ea2 --- /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 + } + +}