diff options
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r-- | src/dotty/tools/dotc/ast/tpd.scala | 16 | ||||
-rw-r--r-- | src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 119 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ErrorReporting.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 491 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ProtoTypes.scala | 384 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 18 |
10 files changed, 537 insertions, 503 deletions
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 95f5b49be..3a0790c9c 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -68,7 +68,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } def Literal(const: Constant)(implicit ctx: Context): Literal = - untpd.Literal(const).withType(const.tpe).checked + typedLiteral(untpd.Literal(const)) + + def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = + tree.withType { + tree.const.tag match { + case UnitTag => defn.UnitType + case NullTag => defn.NullType + case _ => ConstantType(tree.const) + } + } def unitLiteral(implicit ctx: Context): Literal = Literal(Constant(())) @@ -76,6 +85,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def New(tpt: Tree)(implicit ctx: Context): New = untpd.New(tpt).withType(tpt.tpe).checked + def typedNew(tree: untpd.New)(implicit ctx: Context) = { + ctx.typer.checkClassTypeWithStablePrefix(tree.tpt.tpe, tree.tpt.pos, traitReq = false) + tree.withType(tree.tpt.tpe) + } + def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp)) def Pair(left: Tree, right: Tree)(implicit ctx: Context): Pair = diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2702143c1..5231ccd93 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -7,7 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annot import StdNames.nme import ast.{Trees, untpd} import typer.Namer -import typer.Inferencing.{SelectionProto, ViewProto} +import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto} import Trees._ import scala.annotation.switch @@ -120,7 +120,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case ExprType(result) => return "=> " ~ toText(result) - case typer.Inferencing.FunProto(args, resultType, _) => + case FunProto(args, resultType, _) => return "funproto(" ~ toTextGlobal(args, ", ") ~ "):" ~ toText(resultType) case _ => } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index ce3198c64..26325e57e 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -19,7 +19,7 @@ import ErrorReporting._ import Trees._ import Names._ import StdNames._ -import Inferencing._ +import ProtoTypes._ import EtaExpansion._ import collection.mutable import reflect.ClassTag diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala new file mode 100644 index 000000000..2fa0d5519 --- /dev/null +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -0,0 +1,119 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ +import Trees._ +import Constants._ +import Scopes._ +import annotation.unchecked +import util.Positions._ +import util.{Stats, SimpleMap} +import util.common._ +import Decorators._ +import Uniques._ +import ErrorReporting.{errorType, InfoString} +import config.Printers._ +import collection.mutable + +trait Checking { + + import tpd._ + + /** Check that type arguments `args` conform to corresponding bounds in `poly` */ + def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = + for ((arg, bounds) <- args zip poly.paramBounds) { + def notConforms(which: String, bound: Type) = + ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) + if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi) + if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) + } + + /** Check that type `tp` is stable. + * @return The type itself + */ + def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = + if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos) + + /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is + * false check that `tp` is a trait. + * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not. + */ + def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = + tp.underlyingClassRef match { + case tref: TypeRef => + checkStable(tref.prefix, pos) + if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos) + tp + case _ => + ctx.error(i"$tp is not a class type", pos) + defn.ObjectClass.typeRef + } + + /** Check that (return) type of implicit definition is not empty */ + def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { + case TypeTree(original) if original.isEmpty => + val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" + ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) + case _ => + } + + /** Check that a non-implicit parameter making up the first parameter section of an + * implicit conversion is not a singleton type. + */ + def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { + case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => + if (vparam.tpt.tpe.isInstanceOf[SingletonType]) + ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) + case _ => + } + + /** Check that any top-level type arguments in this type are feasible, i.e. that + * their lower bound conforms to their upper cound. If a type argument is + * infeasible, issue and error and continue with upper bound. + */ + def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { + case tp: RefinedType => + tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) + case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => + ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos) + tp.derivedTypeAlias(hi) + case _ => + tp + } + + /** Check that class does not define */ + def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { + val seen = new mutable.HashMap[Name, List[Symbol]] { + override def default(key: Name) = Nil + } + typr.println(i"check no double defs $cls") + for (decl <- cls.info.decls) { + for (other <- seen(decl.name)) { + typr.println(i"conflict? $decl $other") + if (decl.signature matches other.signature) { + def doubleDefError(decl: Symbol, other: Symbol): Unit = { + def ofType = if (decl.isType) "" else i": ${other.info}" + def explanation = + if (!decl.isSourceMethod) "" + else "\n (both definitions have the same erased type signature)" + ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos) + } + if (decl is Synthetic) doubleDefError(other, decl) + else doubleDefError(decl, other) + } + if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { + ctx.error(i"two or more overloaded variants of $decl have default arguments") + decl resetFlag HasDefaultParams + } + } + seen(decl.name) = decl :: seen(decl.name) + } + } + + def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { + ??? // to be done in later phase: check that class `cls` is legal in a new. + } +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 032190625..397285f6a 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -5,7 +5,7 @@ package typer import ast._ import core._ import Trees._ -import Types._, Inferencing._, Contexts._, Decorators._, Denotations._, Symbols._ +import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ import Applications._, Implicits._ import util.Positions._ import printing.Showable diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 008fe8966..b939c1b0d 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -20,8 +20,8 @@ import Decorators._ import Names._ import StdNames._ import Constants._ -import Inferencing._ import Applications._ +import ProtoTypes._ import ErrorReporting._ import Hashable._ import config.Config diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index ea3109afa..6d9afecab 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -8,6 +8,7 @@ import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps import Trees._ import Constants._ import Scopes._ +import ProtoTypes._ import annotation.unchecked import util.Positions._ import util.{Stats, SimpleMap} @@ -18,291 +19,10 @@ import ErrorReporting.{errorType, InfoString} import config.Printers._ import collection.mutable -object Inferencing { +trait Inferencing { this: Checking => import tpd._ - /** A trait defining an `isCompatible` method. */ - trait Compatibility { - - /** Is there an implicit conversion from `tp` to `pt`? */ - def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean - - /** A type `tp` is compatible with a type `pt` if one of the following holds: - * 1. `tp` is a subtype of `pt` - * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type - * 3. there is an implicit conversion from `tp` to `pt`. - */ - def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = - tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt) - - /** Test compatibility after normalization in a fresh typerstate. */ - def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.withExploreTyperState - isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx) - } - - /** Check that the result type of the current method - * fits the given expected result type. - */ - def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { - case FunProto(_, result, _) => - mt match { - case mt: MethodType => - mt.isDependent || constrainResult(mt.resultType, pt.resultType) - case _ => - true - } - case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) => - mt match { - case mt: MethodType => - mt.isDependent || isCompatible(normalize(mt, pt), pt) - case _ => - isCompatible(mt, pt) - } - case _ => - true - } - } - - object NoViewsAllowed extends Compatibility { - override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false - } - - /** A prototype for expressions [] that are part of a selection operation: - * - * [ ].name: proto - */ - abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) - extends CachedProxyType with ProtoType with ValueTypeOrProto { - - override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { - name == nme.WILDCARD || { - val mbr = tp1.member(name) - def qualifies(m: SingleDenotation) = compat.normalizedCompatible(m.info, memberProto) - mbr match { // hasAltWith inlined for performance - case mbr: SingleDenotation => mbr.exists && qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - } - } - - def underlying(implicit ctx: Context) = WildcardType - - def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = - if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this - else SelectionProto(name, memberProto, compat) - - override def equals(that: Any): Boolean = that match { - case that: SelectionProto => - (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) - case _ => - false - } - - def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) - - override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0) - } - - class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) - - object SelectionProto { - def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { - val selproto = new CachedSelectionProto(name, memberProto, compat) - if (compat eq NoViewsAllowed) unique(selproto) else selproto - } - } - - /** Create a selection proto-type, but only one level deep; - * treat constructors specially - */ - def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) = - if (name.isConstructorName) WildcardType - else tp match { - case tp: UnapplyFunProto => new UnapplySelectionProto(name) - case tp: ProtoType => SelectionProto(name, WildcardType, typer) - case _ => SelectionProto(name, tp, typer) - } - - /** A prototype for expressions [] that are in some unspecified selection operation - * - * [].?: ? - * - * Used to indicate that expression is in a context where the only valid - * operation is further selection. In this case, the expression need not be a value. - * @see checkValue - */ - object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) - - /** A prototype for selections in pattern constructors */ - class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) - - trait ApplyingProto extends ProtoType - - /** A prototype for expressions that appear in function position - * - * [](args): resultType - */ - case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) - extends UncachedGroundType with ApplyingProto { - private var myTypedArgs: List[Tree] = Nil - - /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ - private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty - - def isMatchedBy(tp: Type)(implicit ctx: Context) = - typer.isApplicable(tp, Nil, typedArgs, resultType) - - def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) = - if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this - else new FunProto(args, resultType, typer) - - def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty - - /** The typed arguments. This takes any arguments already typed using - * `typedArg` into account. - */ - def typedArgs: List[Tree] = { - if (!argsAreTyped) - myTypedArgs = args mapconserve { arg => - val targ = myTypedArg(arg) - if (targ != null) targ else typer.typed(arg) - } - myTypedArgs - } - - /** Type single argument and remember the unadapted result in `myTypedArg`. - * used to avoid repeated typings of trees when backtracking. - */ - def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = { - var targ = myTypedArg(arg) - if (targ == null) { - val counts = ctx.reporter.errorCounts - targ = typer.typedUnadapted(arg, formal) - if (ctx.reporter.wasSilent(counts)) - myTypedArg = myTypedArg.updated(arg, targ) - } - typer.adapt(targ, formal) - } - - override def toString = s"FunProto(${args mkString ","} => $resultType)" - - def map(tm: TypeMap)(implicit ctx: Context): FunProto = - derivedFunProto(args, tm(resultType), typer) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType) - } - - /** A prototype for implicitly inferred views: - * - * []: argType => resultType - */ - abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) - extends CachedGroundType with ApplyingProto { - def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ { - ctx.typer.isApplicable(tp, argType :: Nil, resultType) - } - - def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = - if ((argType eq this.argType) && (resultType eq this.resultType)) this - else ViewProto(argType, resultType) - - def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType) - - override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = - AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets - } - - class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) { - override def computeHash = doHash(argType, resultType) - } - - object ViewProto { - def apply(argType: Type, resultType: Type)(implicit ctx: Context) = - unique(new CachedViewProto(argType, resultType)) - } - - class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer) - - /** A prototype for expressions [] that are type-parameterized: - * - * [] [targs] resultType - */ - case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType { - override def isMatchedBy(tp: Type)(implicit ctx: Context) = { - def isInstantiatable(tp: Type) = tp.widen match { - case PolyType(paramNames) => paramNames.length == targs.length - case _ => false - } - isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info)) - } - - def derivedPolyProto(targs: List[Type], resultType: Type) = - if ((targs eq this.targs) && (resultType eq this.resultType)) this - else PolyProto(targs, resultType) - - def map(tm: TypeMap)(implicit ctx: Context): PolyProto = - derivedPolyProto(targs mapConserve tm, tm(resultType)) - - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = - ta(ta.foldOver(x, targs), resultType) - } - - /** A prototype for expressions [] that are known to be functions: - * - * [] _ - */ - object AnyFunctionProto extends UncachedGroundType with ProtoType { - def isMatchedBy(tp: Type)(implicit ctx: Context) = true - def map(tm: TypeMap)(implicit ctx: Context) = this - def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x - } - - /** The normalized form of a type - * - unwraps polymorphic types, tracking their parameters in the current constraint - * - skips implicit parameters - * - converts non-dependent method types to the corresponding function types - * - dereferences parameterless method types - * - dereferences nullary method types provided the corresponding function type - * is not a subtype of the expected type. - * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a - * def toString(): String - * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit - * insertion, with a nice explosion of inference search because of course every implicit result has some sort - * of toString method. The problem is solved by dereferencing nullary method types if the corresponding - * function type is not compatible with the prototype. - */ - def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") { - tp.widenSingleton match { - case poly: PolyType => normalize(constrained(poly).resultType, pt) - case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ => - if (mt.isImplicit) mt.resultType - else { - val rt = normalize(mt.resultType, pt) - if (pt.isInstanceOf[ApplyingProto]) - mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) - else { - val ft = defn.FunctionType(mt.paramTypes, rt) - if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt - } - } - case et: ExprType => et.resultType - case _ => tp - } - } - - /** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ - object ForceDegree extends Enumeration { - val none, // don't force type variables - noBottom, // force type variables, fail if forced to Nothing or Null - all = Value // force type variables, don't fail - } - /** Is type fully defined, meaning the type does not contain wildcard types * or uninstantiated type variables. As a side effect, this will minimize * any uninstantiated type variables, according to the given force degree, @@ -395,54 +115,6 @@ object Inferencing { case _ => NoType } - /** Check that type arguments `args` conform to corresponding bounds in `poly` */ - def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = - for ((arg, bounds) <- args zip poly.paramBounds) { - def notConforms(which: String, bound: Type) = - ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) - if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi) - if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) - } - - /** Check that type `tp` is stable. - * @return The type itself - */ - def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = - if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos) - - /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is - * false check that `tp` is a trait. - * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not. - */ - def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = - tp.underlyingClassRef match { - case tref: TypeRef => - checkStable(tref.prefix, pos) - if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos) - tp - case _ => - ctx.error(i"$tp is not a class type", pos) - defn.ObjectClass.typeRef - } - - /** Check that (return) type of implicit definition is not empty */ - def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { - case TypeTree(original) if original.isEmpty => - val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" - ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) - case _ => - } - - /** Check that a non-implicit parameter making up the first parameter section of an - * implicit conversion is not a singleton type. - */ - def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match { - case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) => - if (vparam.tpt.tpe.isInstanceOf[SingletonType]) - ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos) - case _ => - } - /** Ensure that the first type in a list of parent types Ps points to a non-trait class. * If that's not already the case, add one. The added class type CT is determined as follows. * First, let C be the unique class such that @@ -484,134 +156,6 @@ object Inferencing { TypeTree(checkFeasible(first, pos, i"\n in inferred parent $first")).withPos(pos) :: parents } - /** Check that any top-level type arguments in this type are feasible, i.e. that - * their lower bound conforms to their upper cound. If a type argument is - * infeasible, issue and error and continue with upper bound. - */ - def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { - case tp: RefinedType => - tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) - case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => - ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos) - tp.derivedTypeAlias(hi) - case _ => - tp - } - - /** Check that class does not define */ - def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { - val seen = new mutable.HashMap[Name, List[Symbol]] { - override def default(key: Name) = Nil - } - typr.println(i"check no double defs $cls") - for (decl <- cls.info.decls) { - for (other <- seen(decl.name)) { - typr.println(i"conflict? $decl $other") - if (decl.signature matches other.signature) { - def doubleDefError(decl: Symbol, other: Symbol): Unit = { - def ofType = if (decl.isType) "" else i": ${other.info}" - def explanation = - if (!decl.isSourceMethod) "" - else "\n (both definitions have the same erased type signature)" - ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos) - } - if (decl is Synthetic) doubleDefError(other, decl) - else doubleDefError(decl, other) - } - if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { - ctx.error(i"two or more overloaded variants of $decl have default arguments") - decl resetFlag HasDefaultParams - } - } - seen(decl.name) = decl :: seen(decl.name) - } - } - - def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { - ??? // to be done in later phase: check that class `cls` is legal in a new. - } - - /** Approximate occurrences of parameter types and uninstantiated typevars - * by wildcard types. - */ - final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { - case tp: NamedType => // default case, inlined for speed - if (tp.symbol.isStatic) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap)) - case tp: RefinedType => // default case, inlined for speed - tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) - case tp: TypeBounds if tp.lo eq tp.hi => // default case, inlined for speed - tp.derivedTypeAlias(wildApprox(tp.lo, theMap)) - case PolyParam(pt, pnum) => - WildcardType(wildApprox(pt.paramBounds(pnum)).bounds) - case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) wildApprox(inst) - else ctx.typerState.constraint.at(tp.origin) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => WildcardType - } - case tp: AndType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - def wildBounds(tp: Type) = - if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) - else - tp.derivedAndType(tp1a, tp2a) - case tp: OrType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(tp1a.bounds | tp2a.bounds) - else - tp.derivedOrType(tp1a, tp2a) - case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) - case tp: ViewProto => - tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) - case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed - tp - case _ => - (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) - } - - private[Inferencing] class WildApproxMap(implicit ctx: Context) extends TypeMap { - def apply(tp: Type) = wildApprox(tp, this) - } - - /** Add all parameters in given polytype `pt` to the constraint's domain. - * If the constraint contains already some of these parameters in its domain, - * make a copy of the polytype and add the copy's type parameters instead. - * Return either the original polytype, or the copy, if one was made. - * Also, if `owningTree` is non-empty, add a type variable for each parameter. - * @return The added polytype, and the list of created type variables. - */ - def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { - val state = ctx.typerState - def howmany = if (owningTree.isEmpty) "no" else "some" - def committable = if (ctx.typerState.isCommittable) "committable" else "uncommittable" - assert(owningTree.isEmpty != ctx.typerState.isCommittable, - s"inconsistent: $howmany typevars were added to $committable constraint ${state.constraint}") - - def newTypeVars(pt: PolyType): List[TypeVar] = - for (n <- (0 until pt.paramNames.length).toList) - yield new TypeVar(PolyParam(pt, n), state, owningTree) - - val added = - if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) - else pt - val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) - state.constraint = state.constraint.add(added, tvars) - (added, tvars) - } - - /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ - def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 - /** Interpolate those undetermined type variables in the widened type of this tree * which are introduced by type application contained in the tree. * If such a variable appears covariantly in type `tp` or does not appear at all, @@ -665,31 +209,12 @@ object Inferencing { } result } - - private lazy val dummyTree = untpd.Literal(Constant(null)) - - /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ - def dummyTreeOfType(tp: Type): Tree = dummyTree withTypeUnchecked tp } -/* not needed right now - - def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match { - case formal :: formals1 => - actuals match { - case actual :: actuals1 => actual <:< formal && isSubTypes(actuals1, formals1) - case _ => false - } - case nil => - actuals.isEmpty - } +/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ +object ForceDegree extends Enumeration { + val none, // don't force type variables + noBottom, // force type variables, fail if forced to Nothing or Null + all = Value // force type variables, don't fail +} - def formalParameters[T](mtp: MethodType, actuals: List[T])(isRepeated: T => Boolean)(implicit ctx: Context) = - if (mtp.isVarArgs && !(actuals.nonEmpty && isRepeated(actuals.last))) { - val leading = mtp.paramTypes.init - val repeated = mtp.paramTypes.last.typeArgs.head - val trailing = List.fill(actuals.length - leading.length)(repeated) - leading ++ trailing - } - else mtp.paramTypes - */
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 463b7e71e..7d1e0629e 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -7,7 +7,7 @@ import ast._ import Trees._, Constants._, StdNames._, Scopes._, Denotations._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ import ast.desugar, ast.desugar._ -import Inferencing._ +import ProtoTypes._ import util.Positions._ import util.{Attachment, SourcePosition, DotClass} import collection.mutable diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala new file mode 100644 index 000000000..87c556fdc --- /dev/null +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -0,0 +1,384 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ +import Trees._ +import Constants._ +import Scopes._ +import annotation.unchecked +import util.Positions._ +import util.{Stats, SimpleMap} +import util.common._ +import Decorators._ +import Uniques._ +import ErrorReporting.{errorType, InfoString} +import config.Printers._ +import collection.mutable + +object ProtoTypes { + + import tpd._ + + /** A trait defining an `isCompatible` method. */ + trait Compatibility { + + /** Is there an implicit conversion from `tp` to `pt`? */ + def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean + + /** A type `tp` is compatible with a type `pt` if one of the following holds: + * 1. `tp` is a subtype of `pt` + * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type + * 3. there is an implicit conversion from `tp` to `pt`. + */ + def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = + tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt) + + /** Test compatibility after normalization in a fresh typerstate. */ + def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { + val nestedCtx = ctx.fresh.withExploreTyperState + isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx) + } + + /** Check that the result type of the current method + * fits the given expected result type. + */ + def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { + case FunProto(_, result, _) => + mt match { + case mt: MethodType => + mt.isDependent || constrainResult(mt.resultType, pt.resultType) + case _ => + true + } + case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) => + mt match { + case mt: MethodType => + mt.isDependent || isCompatible(normalize(mt, pt), pt) + case _ => + isCompatible(mt, pt) + } + case _ => + true + } + } + + object NoViewsAllowed extends Compatibility { + override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false + } + + /** A prototype for expressions [] that are part of a selection operation: + * + * [ ].name: proto + */ + abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) + extends CachedProxyType with ProtoType with ValueTypeOrProto { + + override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { + name == nme.WILDCARD || { + val mbr = tp1.member(name) + def qualifies(m: SingleDenotation) = compat.normalizedCompatible(m.info, memberProto) + mbr match { // hasAltWith inlined for performance + case mbr: SingleDenotation => mbr.exists && qualifies(mbr) + case _ => mbr hasAltWith qualifies + } + } + } + + def underlying(implicit ctx: Context) = WildcardType + + def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = + if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this + else SelectionProto(name, memberProto, compat) + + override def equals(that: Any): Boolean = that match { + case that: SelectionProto => + (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) + case _ => + false + } + + def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat) + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto) + + override def computeHash = addDelta(doHash(name, memberProto), if (compat == NoViewsAllowed) 1 else 0) + } + + class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) + + object SelectionProto { + def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { + val selproto = new CachedSelectionProto(name, memberProto, compat) + if (compat eq NoViewsAllowed) unique(selproto) else selproto + } + } + + /** Create a selection proto-type, but only one level deep; + * treat constructors specially + */ + def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) = + if (name.isConstructorName) WildcardType + else tp match { + case tp: UnapplyFunProto => new UnapplySelectionProto(name) + case tp: ProtoType => SelectionProto(name, WildcardType, typer) + case _ => SelectionProto(name, tp, typer) + } + + /** A prototype for expressions [] that are in some unspecified selection operation + * + * [].?: ? + * + * Used to indicate that expression is in a context where the only valid + * operation is further selection. In this case, the expression need not be a value. + * @see checkValue + */ + object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) + + /** A prototype for selections in pattern constructors */ + class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) + + trait ApplyingProto extends ProtoType + + /** A prototype for expressions that appear in function position + * + * [](args): resultType + */ + case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) + extends UncachedGroundType with ApplyingProto { + private var myTypedArgs: List[Tree] = Nil + + /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ + private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty + + def isMatchedBy(tp: Type)(implicit ctx: Context) = + typer.isApplicable(tp, Nil, typedArgs, resultType) + + def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) = + if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this + else new FunProto(args, resultType, typer) + + def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty + + /** The typed arguments. This takes any arguments already typed using + * `typedArg` into account. + */ + def typedArgs: List[Tree] = { + if (!argsAreTyped) + myTypedArgs = args mapconserve { arg => + val targ = myTypedArg(arg) + if (targ != null) targ else typer.typed(arg) + } + myTypedArgs + } + + /** Type single argument and remember the unadapted result in `myTypedArg`. + * used to avoid repeated typings of trees when backtracking. + */ + def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = { + var targ = myTypedArg(arg) + if (targ == null) { + val counts = ctx.reporter.errorCounts + targ = typer.typedUnadapted(arg, formal) + if (ctx.reporter.wasSilent(counts)) + myTypedArg = myTypedArg.updated(arg, targ) + } + typer.adapt(targ, formal) + } + + override def toString = s"FunProto(${args mkString ","} => $resultType)" + + def map(tm: TypeMap)(implicit ctx: Context): FunProto = + derivedFunProto(args, tm(resultType), typer) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType) + } + + /** A prototype for implicitly inferred views: + * + * []: argType => resultType + */ + abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) + extends CachedGroundType with ApplyingProto { + def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ { + ctx.typer.isApplicable(tp, argType :: Nil, resultType) + } + + def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = + if ((argType eq this.argType) && (resultType eq this.resultType)) this + else ViewProto(argType, resultType) + + def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType) + + override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = + AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets + } + + class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) { + override def computeHash = doHash(argType, resultType) + } + + object ViewProto { + def apply(argType: Type, resultType: Type)(implicit ctx: Context) = + unique(new CachedViewProto(argType, resultType)) + } + + class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto( + untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer) + + /** A prototype for expressions [] that are type-parameterized: + * + * [] [targs] resultType + */ + case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType { + override def isMatchedBy(tp: Type)(implicit ctx: Context) = { + def isInstantiatable(tp: Type) = tp.widen match { + case PolyType(paramNames) => paramNames.length == targs.length + case _ => false + } + isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info)) + } + + def derivedPolyProto(targs: List[Type], resultType: Type) = + if ((targs eq this.targs) && (resultType eq this.resultType)) this + else PolyProto(targs, resultType) + + def map(tm: TypeMap)(implicit ctx: Context): PolyProto = + derivedPolyProto(targs mapConserve tm, tm(resultType)) + + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = + ta(ta.foldOver(x, targs), resultType) + } + + /** A prototype for expressions [] that are known to be functions: + * + * [] _ + */ + object AnyFunctionProto extends UncachedGroundType with ProtoType { + def isMatchedBy(tp: Type)(implicit ctx: Context) = true + def map(tm: TypeMap)(implicit ctx: Context) = this + def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x + } + + /** Add all parameters in given polytype `pt` to the constraint's domain. + * If the constraint contains already some of these parameters in its domain, + * make a copy of the polytype and add the copy's type parameters instead. + * Return either the original polytype, or the copy, if one was made. + * Also, if `owningTree` is non-empty, add a type variable for each parameter. + * @return The added polytype, and the list of created type variables. + */ + def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { + val state = ctx.typerState + def howmany = if (owningTree.isEmpty) "no" else "some" + def committable = if (ctx.typerState.isCommittable) "committable" else "uncommittable" + assert(owningTree.isEmpty != ctx.typerState.isCommittable, + s"inconsistent: $howmany typevars were added to $committable constraint ${state.constraint}") + + def newTypeVars(pt: PolyType): List[TypeVar] = + for (n <- (0 until pt.paramNames.length).toList) + yield new TypeVar(PolyParam(pt, n), state, owningTree) + + val added = + if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) + else pt + val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) + state.constraint = state.constraint.add(added, tvars) + (added, tvars) + } + + /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ + def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 + + /** The normalized form of a type + * - unwraps polymorphic types, tracking their parameters in the current constraint + * - skips implicit parameters + * - converts non-dependent method types to the corresponding function types + * - dereferences parameterless method types + * - dereferences nullary method types provided the corresponding function type + * is not a subtype of the expected type. + * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a + * def toString(): String + * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit + * insertion, with a nice explosion of inference search because of course every implicit result has some sort + * of toString method. The problem is solved by dereferencing nullary method types if the corresponding + * function type is not compatible with the prototype. + */ + def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") { + tp.widenSingleton match { + case poly: PolyType => normalize(constrained(poly).resultType, pt) + case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ => + if (mt.isImplicit) mt.resultType + else { + val rt = normalize(mt.resultType, pt) + if (pt.isInstanceOf[ApplyingProto]) + mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) + else { + val ft = defn.FunctionType(mt.paramTypes, rt) + if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt + } + } + case et: ExprType => et.resultType + case _ => tp + } + } + + /** Approximate occurrences of parameter types and uninstantiated typevars + * by wildcard types. + */ + final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { + case tp: NamedType => // default case, inlined for speed + if (tp.symbol.isStatic) tp + else tp.derivedSelect(wildApprox(tp.prefix, theMap)) + case tp: RefinedType => // default case, inlined for speed + tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) + case tp: TypeBounds if tp.lo eq tp.hi => // default case, inlined for speed + tp.derivedTypeAlias(wildApprox(tp.lo, theMap)) + case PolyParam(pt, pnum) => + WildcardType(wildApprox(pt.paramBounds(pnum)).bounds) + case MethodParam(mt, pnum) => + WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) + case tp: TypeVar => + val inst = tp.instanceOpt + if (inst.exists) wildApprox(inst) + else ctx.typerState.constraint.at(tp.origin) match { + case bounds: TypeBounds => wildApprox(WildcardType(bounds)) + case NoType => WildcardType + } + case tp: AndType => + val tp1a = wildApprox(tp.tp1) + val tp2a = wildApprox(tp.tp2) + def wildBounds(tp: Type) = + if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) + else + tp.derivedAndType(tp1a, tp2a) + case tp: OrType => + val tp1a = wildApprox(tp.tp1) + val tp2a = wildApprox(tp.tp2) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(tp1a.bounds | tp2a.bounds) + else + tp.derivedOrType(tp1a, tp2a) + case tp: SelectionProto => + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) + case tp: ViewProto => + tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) + case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed + tp + case _ => + (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) + } + + private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap { + def apply(tp: Type) = wildApprox(tp, this) + } + + private lazy val dummyTree = untpd.Literal(Constant(null)) + + /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ + def dummyTreeOfType(tp: Type): Tree = dummyTree withTypeUnchecked tp +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4c4920e5b..12d349ce4 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -9,7 +9,7 @@ import Constants._ import StdNames._ import Scopes._ import Denotations._ -import Inferencing._ +import ProtoTypes._ import Contexts._ import Symbols._ import Types._ @@ -20,7 +20,6 @@ import NameOps._ import Flags._ import Decorators._ import ErrorReporting._ -import Inferencing.{FunProto, PolyProto, Compatibility, normalize} import EtaExpansion.etaExpand import util.Positions._ import util.common._ @@ -49,7 +48,7 @@ object Typer { } } -class Typer extends Namer with Applications with Implicits { +class Typer extends Namer with Applications with Implicits with Inferencing with Checking { import Typer._ import tpd.{cpy => _, _} @@ -414,13 +413,7 @@ class Typer extends Namer with Applications with Implicits { } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { - tree.withType { - tree.const.tag match { - case UnitTag => defn.UnitType - case NullTag => defn.NullType - case _ => ConstantType(tree.const) - } - } + tpd.typedLiteral(tree) } def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") { @@ -431,10 +424,9 @@ class Typer extends Namer with Applications with Implicits { val clsDef = TypeDef(Modifiers(Final), x, templ) typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => - val tpt1 = typedType(tree.tpt) - val clsref = checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + val tpt1 = typedType(tree.tpt) + tpd.typedNew(cpy.New(tree, tpt1)) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) - cpy.New(tree, tpt1).withType(tpt1.tpe) } } |