diff options
Diffstat (limited to 'src')
26 files changed, 284 insertions, 166 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index a36743b59..bf3dbb232 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -9,11 +9,10 @@ import Scopes._ import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks} import reporting.{Reporter, ConsoleReporter} import Phases.Phase -import dotty.tools.dotc.transform._ -import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} -import dotty.tools.dotc.core.DenotTransformers.DenotTransformer -import dotty.tools.dotc.core.Denotations.SingleDenotation - +import transform._ +import transform.TreeTransforms.{TreeTransform, TreeTransformer} +import core.DenotTransformers.DenotTransformer +import core.Denotations.SingleDenotation import dotty.tools.backend.jvm.{LabelDefs, GenBCode} diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index d1f101283..33205dfd4 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -132,7 +132,7 @@ object desugar { case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) => for (cxbound <- cxbounds) { val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param - val epname = (nme.EVIDENCE_PARAM_PREFIX.toString + epbuf.length).toTermName + val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit) } cpy.TypeDef(tparam)(rhs = tbounds) @@ -236,10 +236,12 @@ object desugar { // prefixed by type or val). `tparams` and `vparamss` are the type parameters that // go in `constr`, the constructor after desugaring. + val isCaseClass = mods.is(Case) && !mods.is(Module) + val constrTparams = constr1.tparams map toDefParam val constrVparamss = if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty - if (mods is Case) + if (isCaseClass) ctx.error("case class needs to have at least one parameter list", cdef.pos) ListOfNil } @@ -287,7 +289,7 @@ object desugar { // neg/t1843-variances.scala for a test case. The test would give // two errors without @uncheckedVariance, one of them spurious. val caseClassMeths = - if (mods is Case) { + if (isCaseClass) { def syntheticProperty(name: TermName, rhs: Tree) = DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic) val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) @@ -326,9 +328,9 @@ object desugar { if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) } - // Case classes get a ProductN parent + // Case classes and case objects get a ProductN parent var parents1 = parents - if ((mods is Case) && arity <= Definitions.MaxTupleArity) + if (mods.is(Case) && arity <= Definitions.MaxTupleArity) parents1 = parents1 :+ productConstr(arity) // The thicket which is the desugared version of the companion object @@ -350,7 +352,7 @@ object desugar { // (T11, ..., T1N) => ... => (TM1, ..., TMN) => C // For all other classes, the parent is AnyRef. val companions = - if (mods is Case) { + if (isCaseClass) { val parent = if (constrTparams.nonEmpty || constrVparamss.length > 1 || @@ -386,7 +388,7 @@ object desugar { ctx.error("implicit classes may not be toplevel", cdef.pos) Nil } - else if (mods is Case) { + else if (isCaseClass) { ctx.error("implicit classes may not be case classes", cdef.pos) Nil } @@ -407,7 +409,7 @@ object desugar { val originalTparams = constr1.tparams.toIterator val originalVparams = constr1.vparamss.toIterator.flatten val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods)) - val caseAccessor = if (mods is Case) CaseAccessor else EmptyFlags + val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor)) cpy.TypeDef(cdef)( rhs = cpy.Template(impl)(constr, parents1, self1, @@ -453,7 +455,7 @@ object desugar { .withPos(tmpl.self.pos orElse tmpl.pos.startPos) val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body) val cls = TypeDef(clsName, clsTmpl) - .withMods(mods.toTypeFlags & AccessOrSynthetic | ModuleClassCreationFlags) + .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags) Thicket(modul, classDef(cls)) } } @@ -556,14 +558,20 @@ object desugar { DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic), Closure(Nil, Ident(nme.ANON_FUN), EmptyTree)) - /** Expand partial function + /** If `nparams` == 1, expand partial function + * * { cases } * ==> - * x$0 => x$0 match { cases } + * x$1 => x$1 match { cases } + * + * If `nparams` != 1, expand instead to + * + * (x$1, ..., x$n) => (x$0, ..., x${n-1}) match { cases } */ - def makeCaseLambda(cases: List[CaseDef])(implicit ctx: Context) = { - val param = makeSyntheticParameter() - Function(param :: Nil, Match(Ident(param.name), cases)) + def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = { + val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) + val selector = makeTuple(params.map(p => Ident(p.name))) + Function(params, Match(selector, cases)) } /** Add annotation with class `cls` to tree: diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 05fefc8b4..62b071372 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -18,6 +18,7 @@ class ScalaSettings extends Settings.SettingGroup { */ val dependencyfile = StringSetting("-dependencyfile", "file", "Set dependency tracking file.", ".scala_dependencies") val deprecation = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.") + val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.") val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding) val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.") val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.") diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index d5fdba1af..522240b0f 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -153,6 +153,14 @@ object Contexts { protected def gadt_=(gadt: GADTMap) = _gadt = gadt def gadt: GADTMap = _gadt + /**The current fresh name creator */ + private[this] var _freshNames: FreshNameCreator = _ + protected def freshNames_=(freshNames: FreshNameCreator) = _freshNames = freshNames + def freshNames: FreshNameCreator = _freshNames + + def freshName(prefix: String = ""): String = freshNames.newName(prefix) + def freshName(prefix: Name): String = freshName(prefix.toString) + /** A map in which more contextual properties can be stored */ private var _moreProperties: Map[String, Any] = _ protected def moreProperties_=(moreProperties: Map[String, Any]) = _moreProperties = moreProperties @@ -423,6 +431,7 @@ object Contexts { def setDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this } def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this } def setSearchHistory(searchHistory: SearchHistory): this.type = { this.searchHistory = searchHistory; this } + def setFreshNames(freshNames: FreshNameCreator): this.type = { this.freshNames = freshNames; this } def setMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this } def setProperty(prop: (String, Any)): this.type = setMoreProperties(moreProperties + prop) @@ -468,6 +477,7 @@ object Contexts { typeAssigner = TypeAssigner runInfo = new RunInfo(this) diagnostics = None + freshNames = new FreshNameCreator.Default moreProperties = Map.empty typeComparer = new TypeComparer(this) searchHistory = new SearchHistory(0, Map()) @@ -498,12 +508,6 @@ object Contexts { /** The platform */ val platform: Platform = new JavaPlatform - /** The standard fresh name creator */ - val freshNames = new FreshNameCreator.Default - - def freshName(prefix: String = ""): String = freshNames.newName(prefix) - def freshName(prefix: Name): String = freshName(prefix.toString) - /** The loader that loads the members of _root_ */ def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index a503a2d23..6f322904e 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -192,6 +192,9 @@ class Definitions { ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef)) lazy val ScalaPredefModule = ctx.requiredModule("scala.Predef") + + lazy val Predef_conforms = ctx.requiredMethod(ScalaPredefModule.moduleClass.asClass, "$conforms") + lazy val ScalaRuntimeModule = ctx.requiredModule("scala.runtime.ScalaRunTime") lazy val ScalaRuntimeClass = ScalaRuntimeModule.moduleClass.asClass @@ -210,7 +213,6 @@ class Definitions { def newRefArrayMethod = ctx.requiredMethod(DottyArraysModule.moduleClass.asClass, "newRefArray") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") - lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) // lazy val FunctionClass: ClassSymbol = ctx.requiredClass("scala.Function") lazy val SingletonClass: ClassSymbol = diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 0dde5de95..1271d7f6a 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -227,6 +227,8 @@ object Flags { /** A trait */ final val Trait = typeFlag(10, "<trait>") + final val LazyOrTrait = Lazy.toCommonFlags + /** A value or variable accessor (getter or setter) */ final val Accessor = termFlag(11, "<accessor>") @@ -315,6 +317,8 @@ object Flags { /** An unpickled Scala 2.x class */ final val Scala2x = typeFlag(26, "<scala-2.x>") + final val SuperAccessorOrScala2x = SuperAccessor.toCommonFlags + /** A method that has default params */ final val DefaultParameterized = termFlag(27, "<defaultparam>") @@ -440,7 +444,7 @@ object Flags { AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | - SelfNameOrImplClass + LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags) // TODO: Should check that FromStartFlags do not change in completion diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 126af9259..20d674b86 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -176,7 +176,7 @@ object SymDenotations { // completions.println(s"completed ${this.debugString}") } - protected[dotc] final def info_=(tp: Type) = { + protected[dotc] def info_=(tp: Type) = { /* // DEBUG def illegal: String = s"illegal type for $this: $tp" if (this is Module) // make sure module invariants that allow moduleClass and sourceModule to work are kept. @@ -1177,6 +1177,11 @@ object SymDenotations { myTypeParams } + override protected[dotc] final def info_=(tp: Type) = { + super.info_=(tp) + myTypeParams = null // changing the info might change decls, and with it typeParams + } + /** The denotations of all parents in this class. */ def classParents(implicit ctx: Context): List[TypeRef] = info match { case classInfo: ClassInfo => classInfo.classParents diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 47935f79e..7274c1f70 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -48,33 +48,43 @@ class TypeApplications(val self: Type) extends AnyVal { * For a typeref referring to a class, the type parameters of the class. * For a typeref referring to a Lambda class, the type parameters of * its right hand side or upper bound. - * For a refinement type, the type parameters of its parent, unless the refinement - * re-binds the type parameter with a type-alias. - * For any other non-singleton type proxy, the type parameters of its underlying type. - * For any other type, the empty list. + * For a refinement type, the type parameters of its parent, dropping + * any type parameter that is-rebound by the refinement. "Re-bind" means: + * The refinement contains a TypeAlias for the type parameter, or + * it introduces bounds for the type parameter, and we are not in the + * special case of a type Lambda, where a LambdaTrait gets refined + * with the bounds on its hk args. See `LambdaAbstract`, where these + * types get introduced, and see `isBoundedLambda` below for the test. */ final def typeParams(implicit ctx: Context): List[TypeSymbol] = /*>|>*/ track("typeParams") /*<|<*/ { self match { - case tp: ClassInfo => - tp.cls.typeParams - case tp: TypeRef => - val tsym = tp.typeSymbol + case self: ClassInfo => + self.cls.typeParams + case self: TypeRef => + val tsym = self.typeSymbol if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) tp.underlying.typeParams + else if (tsym.isAliasType) self.underlying.typeParams else { val lam = LambdaClass(forcing = false) if (lam.exists) lam.typeParams else Nil } - case tp: RefinedType => - val tparams = tp.parent.typeParams - tp.refinedInfo match { - case rinfo: TypeAlias => tparams.filterNot(_.name == tp.refinedName) - case _ => tparams + case self: RefinedType => + def isBoundedLambda(tp: Type): Boolean = tp match { + case tp: TypeRef => tp.typeSymbol.isLambdaTrait + case tp: RefinedType => tp.refinedName.isLambdaArgName && isBoundedLambda(tp.parent) + case _ => false + } + val tparams = self.parent.typeParams + self.refinedInfo match { + case rinfo: TypeBounds if rinfo.isAlias || isBoundedLambda(self) => + tparams.filterNot(_.name == self.refinedName) + case _ => + tparams } - case tp: SingletonType => + case self: SingletonType => Nil - case tp: TypeProxy => - tp.underlying.typeParams + case self: TypeProxy => + self.underlying.typeParams case _ => Nil } @@ -91,23 +101,23 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def rawTypeParams(implicit ctx: Context): List[TypeSymbol] = { self match { - case tp: ClassInfo => - tp.cls.typeParams - case tp: TypeRef => - val tsym = tp.typeSymbol + case self: ClassInfo => + self.cls.typeParams + case self: TypeRef => + val tsym = self.typeSymbol if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) tp.underlying.rawTypeParams + else if (tsym.isAliasType) self.underlying.rawTypeParams else Nil case _: BoundType | _: SingletonType => Nil - case tp: TypeProxy => - tp.underlying.rawTypeParams + case self: TypeProxy => + self.underlying.rawTypeParams case _ => Nil } } - /** If type `tp` is equal, aliased-to, or upperbounded-by a type of the form + /** If type `self` is equal, aliased-to, or upperbounded-by a type of the form * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol. * symbol of that type, otherwise NoSymbol. * @param forcing if set, might force completion. If not, never forces @@ -125,7 +135,7 @@ class TypeApplications(val self: Type) extends AnyVal { NoSymbol }} - /** Is type `tp` equal, aliased-to, or upperbounded-by a type of the form + /** Is type `self` equal, aliased-to, or upperbounded-by a type of the form * `LambdaXYZ { ... }`? */ def isLambda(implicit ctx: Context): Boolean = @@ -137,7 +147,7 @@ class TypeApplications(val self: Type) extends AnyVal { def isSafeLambda(implicit ctx: Context): Boolean = LambdaClass(forcing = false).exists - /** Is type `tp` a Lambda with all hk$i fields fully instantiated? */ + /** Is type `self` a Lambda with all hk$i fields fully instantiated? */ def isInstantiatedLambda(implicit ctx: Context): Boolean = isSafeLambda && typeParams.isEmpty @@ -225,7 +235,7 @@ class TypeApplications(val self: Type) extends AnyVal { } /** Same as isHK, except we classify all abstract types as HK, - * (they must be, because the are applied). This avoids some forcing and + * (they must be, because they are applied). This avoids some forcing and * CyclicReference errors of the standard isHK. */ def isKnownHK(tp: Type): Boolean = tp match { diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 77c6805f0..aa643256c 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -5,6 +5,7 @@ package core import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ import SymDenotations._, Denotations.Denotation import config.Printers._ +import util.Positions._ import Decorators._ import StdNames._ import util.SimpleMap @@ -572,6 +573,15 @@ trait TypeOps { this: Context => // TODO: Make standalone object. /** Is auto-tupling enabled? */ def canAutoTuple = !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling) + + def scala2Mode = + featureEnabled(defn.LanguageModuleClass, nme.Scala2) + + def testScala2Mode(msg: String, pos: Position) = { + if (scala2Mode) migrationWarning(msg, pos) + scala2Mode + } + } object TypeOps { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 58a6d226d..bfff1a448 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -606,7 +606,7 @@ object Types { } /** Is this type a primitive value type which can be widened to the primitive value type `that`? */ - def isValueSubType(that: Type)(implicit ctx: Context) = widenExpr match { + def isValueSubType(that: Type)(implicit ctx: Context) = widen match { case self: TypeRef if defn.ScalaValueClasses contains self.symbol => that.widenExpr match { case that: TypeRef if defn.ScalaValueClasses contains that.symbol => @@ -618,6 +618,9 @@ object Types { false } + def relaxed_<:<(that: Type)(implicit ctx: Context) = + (this <:< that) || (this isValueSubType that) + /** Is this type a legal type for a member that overrides another * member of type `that`? This is the same as `<:<`, except that * the types ()T and => T are identified, and T is seen as overriding @@ -846,14 +849,16 @@ object Types { * (*) normalizes means: follow instantiated typevars and aliases. */ def lookupRefined(name: Name)(implicit ctx: Context): Type = { - def loop(pre: Type, resolved: List[Name]): Type = pre.stripTypeVar match { + def loop(pre: Type): Type = pre.stripTypeVar match { case pre: RefinedType => object instantiate extends TypeMap { var isSafe = true def apply(tp: Type): Type = tp match { case TypeRef(RefinedThis(`pre`), name) if name.isLambdaArgName => - val TypeAlias(alias) = member(name).info - alias + member(name).info match { + case TypeAlias(alias) => alias + case _ => isSafe = false; tp + } case tp: TypeVar if !tp.inst.exists => isSafe = false tp @@ -861,23 +866,37 @@ object Types { mapOver(tp) } } - def betaReduce(tp: Type) = { - val lam = pre.parent.LambdaClass(forcing = false) - if (lam.exists && lam.typeParams.forall(tparam => resolved.contains(tparam.name))) { - val reduced = instantiate(tp) + def instArg(tp: Type): Type = tp match { + case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isLambdaArgName => + member(name).info match { + case TypeAlias(alias) => tp.derivedTypeAlias(alias) // needed to keep variance + case bounds => bounds + } + case _ => + instantiate(tp) + } + def instTop(tp: Type): Type = tp.stripTypeVar match { + case tp: RefinedType => + tp.derivedRefinedType(instTop(tp.parent), tp.refinedName, instArg(tp.refinedInfo)) + case _ => + instantiate(tp) + } + /** Reduce rhs of $hkApply to make it stand alone */ + def betaReduce(tp: Type) = + if (pre.parent.isSafeLambda) { + val reduced = instTop(tp) if (instantiate.isSafe) reduced else NoType } else NoType - } pre.refinedInfo match { case TypeAlias(alias) => - if (pre.refinedName ne name) loop(pre.parent, pre.refinedName :: resolved) + if (pre.refinedName ne name) loop(pre.parent) else if (!pre.refinementRefersToThis) alias else alias match { case TypeRef(RefinedThis(`pre`), aliasName) => lookupRefined(aliasName) // (1) case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) } - case _ => loop(pre.parent, resolved) + case _ => loop(pre.parent) } case RefinedThis(binder) => binder.lookupRefined(name) @@ -887,14 +906,14 @@ object Types { WildcardType case pre: TypeRef => pre.info match { - case TypeAlias(alias) => loop(alias, resolved) + case TypeAlias(alias) => loop(alias) case _ => NoType } case _ => NoType } - loop(this, Nil) + loop(this) } /** The type <this . name> , reduced if possible */ @@ -1886,6 +1905,10 @@ object Types { s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.LambdaArgIndex).variance}, ${refinedInfo.variance}") case _ => } + if (Config.checkProjections && + (refinedName == tpnme.hkApply || refinedName.isLambdaArgName) && + parent.noHK) + assert(false, s"illegal refinement of first-order type: $this") this } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 58697c196..86bbc893f 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -148,27 +148,32 @@ class TreePickler(pickler: TastyPickler) { pickleType(tpe.info.bounds.hi) case tpe: WithFixedSym => val sym = tpe.symbol + def pickleRef() = + if (tpe.prefix == NoPrefix) { + writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) + pickleSymRef(sym) + } + else { + assert(tpe.symbol.isClass) + assert(tpe.symbol.is(Flags.Scala2x), tpe.symbol.showLocated) + writeByte(TYPEREF) // should be changed to a new entry that keeps track of prefix, symbol & owner + pickleName(tpe.name) + pickleType(tpe.prefix) + } if (sym.is(Flags.Package)) { writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg) pickleName(qualifiedName(sym)) } - else { - assert(tpe.prefix == NoPrefix) - def pickleRef() = { - writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) - pickleSymRef(sym) - } - if (sym is Flags.BindDefinedType) { - registerDef(sym) - writeByte(BIND) - withLength { - pickleName(sym.name) - pickleType(sym.info) - pickleRef() - } + else if (sym is Flags.BindDefinedType) { + registerDef(sym) + writeByte(BIND) + withLength { + pickleName(sym.name) + pickleType(sym.info) + pickleRef() } - else pickleRef() } + else pickleRef() case tpe: TermRefWithSignature => if (tpe.symbol.is(Flags.Package)) picklePackageRef(tpe.symbol) else { diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 6e405d5f8..9269109ad 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -130,8 +130,18 @@ object Scala2Unpickler { } else { registerCompanionPair(scalacCompanion, denot.classSymbol) } + val declsTypeParams = denot.typeParams + val declsInRightOrder = + if (declsTypeParams.corresponds(tparams)(_.name == _.name)) decls + else { // create new scope with type parameters in right order + val decls1 = newScope + for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name)) + for (sym <- decls) if (!declsTypeParams.contains(sym)) decls1.enter(sym) + decls1 + } - denot.info = ClassInfo(denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) + denot.info = ClassInfo( + denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost) } } @@ -405,6 +415,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas // println(owner.info.decls.toList.map(_.debugString).mkString("\n ")) // !!! DEBUG // } // (5) Create a stub symbol to defer hard failure a little longer. + println(i"***** missing reference, looking for $name in $owner") + println(i"decls = ${owner.info.decls}") new Exception().printStackTrace() ctx.newStubSymbol(owner, name, source) } @@ -451,6 +463,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } def finishSym(sym: Symbol): Symbol = { + if (sym.isClass) sym.setFlag(Scala2x) val owner = sym.owner if (owner.isClass && !( isUnpickleRoot(sym) @@ -536,7 +549,6 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case denot: ClassDenotation => val selfInfo = if (atEnd) NoType else readTypeRef() setClassInfo(denot, tp, selfInfo) - denot setFlag Scala2x case denot => val tp1 = depoly(tp, denot) denot.info = @@ -621,7 +633,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas tp.derivedRefinedType(parent1, name, info) } case tp @ TypeRef(pre, tpnme.hkApply) => - elim(pre) + tp.derivedSelect(elim(pre)) case _ => tp } @@ -687,7 +699,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case _ => } val tycon = - if (isLocal(sym) || pre == NoPrefix) { + if (sym.isClass && sym.is(Scala2x) && !sym.owner.is(Package)) + // used fixed sym for Scala 2 inner classes, because they might be shadowed + TypeRef.withFixedSym(pre, sym.name.asTypeName, sym.asType) + else if (isLocal(sym) || pre == NoPrefix) { val pre1 = if ((pre eq NoPrefix) && (sym is TypeParam)) sym.owner.thisType else pre pre1 select sym } diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index 7b9d29ce3..52bcdb965 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -114,7 +114,7 @@ object JavaParsers { def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean) = { def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { - case (meth: DefDef) :: rest if meth.name.isConstructorName => (meth, rest) + case (meth: DefDef) :: rest if meth.name == CONSTRUCTOR => (meth, rest) case first :: rest => val (constr, tail) = pullOutFirstConstr(rest) (constr, first :: tail) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 4b22eac95..71a03b9e2 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -259,7 +259,11 @@ object Parsers { } /** Cannot use ctx.featureEnabled because accessing the context would force too much */ - private def scala2mode = ctx.settings.language.value.contains(nme.Scala2.toString) + private def testScala2Mode(msg: String, pos: Position = Position(in.offset)) = { + val s2 = ctx.settings.language.value.contains(nme.Scala2.toString) + if (s2) ctx.migrationWarning(msg, source atPos pos) + s2 + } /* ---------- TREE CONSTRUCTION ------------------------------------------- */ @@ -322,6 +326,7 @@ object Parsers { case Ident(name1) => placeholderParams.nonEmpty && name1 == placeholderParams.head.name case Typed(t1, _) => isWildcard(t1) case Annotated(t1, _) => isWildcard(t1) + case Parens(t1) => isWildcard(t1) case _ => false } @@ -1725,12 +1730,13 @@ object Parsers { * DefSig ::= id [DefTypeParamClause] ParamClauses */ def defDefOrDcl(mods: Modifiers): Tree = atPos(tokenRange) { - def atScala2Brace = scala2mode && in.token == LBRACE + def scala2ProcedureSyntax = + testScala2Mode("Procedure syntax no longer supported; `=' should be inserted here") if (in.token == THIS) { in.nextToken() val vparamss = paramClauses(nme.CONSTRUCTOR) val rhs = { - if (!atScala2Brace) accept(EQUALS) + if (!(in.token == LBRACE && scala2ProcedureSyntax)) accept(EQUALS) atPos(in.offset) { constrExpr() } } makeConstructor(Nil, vparamss, rhs).withMods(mods) @@ -1741,10 +1747,22 @@ object Parsers { val vparamss = paramClauses(name) var tpt = fromWithinReturnType(typedOpt()) val rhs = - if (tpt.isEmpty || in.token == EQUALS) { - if (atScala2Brace) tpt = scalaUnit else accept(EQUALS) + if (in.token == EQUALS) { + in.nextToken() + expr + } + else if (!tpt.isEmpty) + EmptyTree + else if (scala2ProcedureSyntax) { + tpt = scalaUnit + if (in.token == LBRACE) expr() + else EmptyTree + } + else { + if (!isExprIntro) syntaxError("missing return type", in.lastOffset) + accept(EQUALS) expr() - } else EmptyTree + } DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1) } } diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 0358f71f6..4a72c2066 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -66,6 +66,9 @@ object Reporter { class DeprecationWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) { def enablingOption(implicit ctx: Context) = ctx.settings.deprecation } + class MigrationWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) { + def enablingOption(implicit ctx: Context) = ctx.settings.migration + } } import Reporter._ @@ -82,6 +85,9 @@ trait Reporting { this: Context => def deprecationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = reporter.report(new DeprecationWarning(msg, pos)) + def migrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new MigrationWarning(msg, pos)) + def uncheckedWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = reporter.report(new UncheckedWarning(msg, pos)) diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 7a25a9870..221d097f9 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -68,10 +68,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // TODO fill in } - /** Check bounds of AppliedTypeTrees. + /** Check bounds of AppliedTypeTrees and TypeApplys. * Replace type trees with TypeTree nodes. * Replace constant expressions with Literal nodes. - * Note: Demanding idempotency instead of purityin literalize is strictly speaking too loose. + * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. * Example * * object O { final val x = 42; println("43") } @@ -114,6 +114,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) norm(tree) + case TypeApply(fn, args) => + Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) + norm(tree) case _ => norm(tree) } diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 98b8357b9..b2d45b661 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -27,7 +27,6 @@ import ValueClasses._ * Unfortunately this phase ended up being not Y-checkable unless types are erased. A cast to an ConstantType(3) or x.type * cannot be rewritten before erasure. */ - trait TypeTestsCasts { import ast.tpd._ diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 0fee17bcd..87bed8895 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -607,7 +607,7 @@ trait Applications extends Compatibility { self: Typer => case pt: PolyType => if (typedArgs.length <= pt.paramBounds.length) typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg) - checkBounds(typedArgs, pt) + Checking.checkBounds(typedArgs, pt) case _ => } assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index fa417beec..3dc7b67df 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -41,6 +41,13 @@ object Checking { d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", arg.pos) + /** Check that type arguments `args` conform to corresponding bounds in `poly` + * Note: This does not check the bounds of AppliedTypeTrees. These + * are handled by method checkBounds in FirstTransform + */ + def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = + checkBounds(args, poly.paramBounds, _.substParams(poly, _)) + /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ @@ -295,13 +302,6 @@ trait Checking { tree } - /** Check that type arguments `args` conform to corresponding bounds in `poly` - * Note: This does not check the bounds of AppliedTypeTrees. These - * are handled by method checkBounds in FirstTransform - */ - def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = - Checking.checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - /** Check that type `tp` is stable. */ def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = if (!tp.isStable && !tp.isErroneous) @@ -398,9 +398,7 @@ trait Checking { if (tpt.tpe.isHK && !ctx.compilationUnit.isJava) { // be more lenient with missing type params in Java, // needed to make pos/java-interop/t1196 work. - val alias = tpt.tpe.dealias - if (alias.isHK) errorTree(tpt, d"missing type parameter for ${tpt.tpe}") - else tpt.withType(alias) + errorTree(tpt, d"missing type parameter for ${tpt.tpe}") } else tpt } @@ -409,7 +407,6 @@ trait NoChecking extends Checking { import tpd._ override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree - override def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = () override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala index f417448bd..e8a0adf19 100644 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -9,6 +9,7 @@ import parsing.Parsers.Parser import config.Printers._ import util.Stats._ import scala.util.control.NonFatal +import util.FreshNameCreator class FrontEnd extends Phase { @@ -28,7 +29,8 @@ class FrontEnd extends Phase { unit.untpdTree = if (unit.isJava) new JavaParser(unit.source).parse() else new Parser(unit.source).parse() - typr.println("parsed:\n" + unit.untpdTree.show) + val printer = if (ctx.settings.Xprint.value.contains("parser")) default else typr + printer.println("parsed:\n" + unit.untpdTree.show) } def enterSyms(implicit ctx: Context) = monitor("indexing") { @@ -46,7 +48,8 @@ class FrontEnd extends Phase { } override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { - val unitContexts = units map (unit => ctx.fresh.setCompilationUnit(unit)) + val unitContexts = for (unit <- units) yield + ctx.fresh.setCompilationUnit(unit).setFreshNames(new FreshNameCreator.Default) unitContexts foreach (parse(_)) record("parsedTrees", ast.Trees.ntrees) unitContexts foreach (enterSyms(_)) diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index e3626fe20..280cc40e9 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -44,19 +44,19 @@ object Implicits { /** Return those references in `refs` that are compatible with type `pt`. */ protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") { - def refMatches(ref: TermRef)(implicit ctx: Context) = { + def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ { def discardForView(tpw: Type, argType: Type): Boolean = tpw match { case mt: MethodType => mt.isImplicit || mt.paramTypes.length != 1 || - !(argType <:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) + !(argType relaxed_<:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) case poly: PolyType => poly.resultType match { case mt: MethodType => mt.isImplicit || mt.paramTypes.length != 1 || - !(argType <:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) + !(argType relaxed_<:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) case rtp => discardForView(wildApprox(rtp), argType) } @@ -65,7 +65,9 @@ object Implicits { case tpw => //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType)) // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}") - !tpw.derivesFrom(defn.FunctionClass(1)) || tpw.isRef(defn.PredefConformsClass) + !tpw.derivesFrom(defn.FunctionClass(1)) || + ref.symbol == defn.Predef_conforms // + // as an implicit conversion, Predef.$conforms is a no-op, so exclude it } def discardForValueType(tpw: Type): Boolean = tpw match { @@ -530,6 +532,25 @@ trait Implicits { self: Typer => case nil => acc } + /** If the (result types of) the expected type, and both alternatives + * are all numeric value types, return the alternative which has + * the smaller numeric subtype as result type, if it exists. + * (This alternative is then discarded). + */ + def numericValueTieBreak(alt1: SearchSuccess, alt2: SearchSuccess): SearchResult = { + def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass + def isProperSubType(tp1: Type, tp2: Type) = + tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1) + val rpt = pt.resultType + val rt1 = alt1.ref.widen.resultType + val rt2 = alt2.ref.widen.resultType + if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2)) + if (isProperSubType(rt1, rt2)) alt1 + else if (isProperSubType(rt2, rt1)) alt2 + else NoImplicitMatches + else NoImplicitMatches + } + /** Convert a (possibly empty) list of search successes into a single search result */ def condense(hits: List[SearchSuccess]): SearchResult = hits match { case best :: alts => @@ -539,7 +560,10 @@ trait Implicits { self: Typer => println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) */ - new AmbiguousImplicits(best.ref, alt.ref, pt, argument) + numericValueTieBreak(best, alt) match { + case eliminated: SearchSuccess => condense(hits.filter(_ ne eliminated)) + case _ => new AmbiguousImplicits(best.ref, alt.ref, pt, argument) + } case None => ctx.runInfo.useCount(best.ref) += 1 best diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 40e919ee5..2b19d9db3 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -32,9 +32,10 @@ object ProtoTypes { * 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`. + * 4. `tp` is a numeric subtype of `pt` (this case applies even if implicit conversions are disabled) */ def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = - tp.widenExpr <:< pt.widenExpr || viewExists(tp, pt) + (tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt) /** Test compatibility after normalization in a fresh typerstate. */ def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index ae854ed9a..3ffbb8079 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -287,7 +287,9 @@ object RefChecks { !member.isAnyOverride) { // (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. - if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) + if (member.name == nme.isDefined && member.is(Synthetic)) // isDefined methods are added automatially, can't have an override preset. + member.setFlag(Override) + else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) emitOverrideError( clazz + " inherits conflicting members:\n " + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 7225ede14..25030012c 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -344,10 +344,9 @@ trait TypeAssigner { def assignType(tree: untpd.Return)(implicit ctx: Context) = tree.withType(defn.NothingType) - def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = { + def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = if (cases.isEmpty) tree.withType(expr.tpe) else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes)) - } def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = tree match { case tree: JavaSeqLiteral => diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 569e4f7d5..c621d96c4 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -220,8 +220,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType else curOwner.thisType.select(name, defDenot) - if (!(curOwner is Package) || (defDenot.symbol is Package) || isDefinedInCurrentUnit(defDenot)) + if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot)) return checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry + else if (defDenot.symbol is Package) + return checkNewOrShadowed(previous orElse found, packageClause) else if (prevPrec < packageClause) return findRef(found, packageClause, ctx)(outer) } @@ -502,6 +504,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } + private def decomposeProtoFunction(pt: Type, defaultArity: Int)(implicit ctx: Context): (List[Type], Type) = pt match { + case _ if defn.isFunctionType(pt) => + (pt.dealias.argInfos.init, pt.dealias.argInfos.last) + case SAMType(meth) => + val mt @ MethodType(_, paramTypes) = meth.info + (paramTypes, mt.resultType) + case _ => + (List.range(0, defaultArity) map alwaysWildcardType, WildcardType) + } + def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) @@ -509,15 +521,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) else { val params = args.asInstanceOf[List[untpd.ValDef]] - val (protoFormals, protoResult): (List[Type], Type) = pt match { - case _ if defn.isFunctionType(pt) => - (pt.dealias.argInfos.init, pt.dealias.argInfos.last) - case SAMType(meth) => - val mt @ MethodType(_, paramTypes) = meth.info - (paramTypes, mt.resultType) - case _ => - (params map alwaysWildcardType, WildcardType) - } + val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length) def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match { case Ident(name) => name == param.name @@ -629,7 +633,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { tree.selector match { case EmptyTree => - typed(desugar.makeCaseLambda(tree.cases) withPos tree.pos, pt) + val (protoFormals, _) = decomposeProtoFunction(pt, 1) + typed(desugar.makeCaseLambda(tree.cases, protoFormals.length) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) val selType = widenForMatchSelector( @@ -848,34 +853,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val args1 = args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] // check that arguments conform to bounds is done in phase PostTyper - val tree1 = assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) - if (tree1.tpe.isHKApply) - for (arg @ TypeBoundsTree(_, _) <- args1) - ctx.error("illegal wildcard type argument; does not correspond to type parameter of a class", arg.pos) - // The reason for outlawing such arguments is illustrated by the following example. - // Say we have - // - // type RMap[A, B] = Map[B, A] - // - // Then - // - // Rmap[_, Int] - // - // translates to - // - // Lambda$I { type hk$0; type hk$1 = Int; type $apply = Map[$hk1, $hk0] } # $apply - // - // Let's call the last type T. You would expect that - // - // Map[Int, String] <: RMap[_, Int] - // - // But that's not the case given the standard subtyping rules. In fact, the rhs reduces to - // - // Map[Int, T # $hk0] - // - // That means the second argument to `Map` is unknown and String is certainly not a subtype of it. - // To avoid the surprise we outlaw problematic wildcard arguments from the start. - tree1 + assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) } } @@ -888,8 +866,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) val lo1 = typed(lo) val hi1 = typed(hi) - if (!(lo1.tpe <:< hi1.tpe)) - ctx.error(d"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) } diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala index 1d3ceaa57..5d3bd4f20 100644 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -110,16 +110,20 @@ class VarianceChecker()(implicit ctx: Context) { private object Traverser extends TreeTraverser { def checkVariance(sym: Symbol) = Validator.validateDefinition(sym) match { case Some(VarianceError(tvar, required)) => - ctx.error( - i"${varianceString(tvar.flags)} $tvar occurs in ${varianceString(required)} position in type ${sym.info} of $sym", - sym.pos) + def msg = i"${varianceString(tvar.flags)} $tvar occurs in ${varianceString(required)} position in type ${sym.info} of $sym" + if (ctx.scala2Mode && sym.owner.isConstructor) + ctx.migrationWarning(s"According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance:\n$msg", sym.pos) + else ctx.error(msg, sym.pos) case None => } override def traverse(tree: Tree)(implicit ctx: Context) = { def sym = tree.symbol // No variance check for private/protected[this] methods/values. - def skip = !sym.exists || sym.is(Local) + def skip = + !sym.exists || + sym.is(Local) || // !!! watch out for protected local! + sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class tree match { case defn: MemberDef if skip => ctx.debuglog(s"Skipping variance check of ${sym.showDcl}") |