diff options
Diffstat (limited to 'compiler/src/dotty/tools/dotc')
38 files changed, 785 insertions, 322 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 4b2ff1bc3..15cb0b665 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -142,16 +142,21 @@ object desugar { val DefDef(name, tparams, vparamss, tpt, rhs) = meth val mods = meth.mods val epbuf = new ListBuffer[ValDef] - val tparams1 = tparams mapConserve { - case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) => + def desugarContextBounds(rhs: Tree): Tree = rhs match { + case ContextBounds(tbounds, cxbounds) => for (cxbound <- cxbounds) { val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit) } - cpy.TypeDef(tparam)(rhs = tbounds) - case tparam => - tparam + tbounds + case PolyTypeTree(tparams, body) => + cpy.PolyTypeTree(rhs)(tparams, desugarContextBounds(body)) + case _ => + rhs + } + val tparams1 = tparams mapConserve { tdef => + cpy.TypeDef(tdef)(rhs = desugarContextBounds(tdef.rhs)) } val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList) @@ -916,7 +921,11 @@ object desugar { val elems = segments flatMap { case ts: Thicket => ts.trees.tail case t => Nil + } map { + case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala + case t => t } + Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) case InfixOp(l, op, r) => if (ctx.mode is Mode.Type) @@ -1079,6 +1088,10 @@ object desugar { collect(tree) case Tuple(trees) => trees foreach collect + case Thicket(trees) => + trees foreach collect + case Block(Nil, expr) => + collect(expr) case _ => } collect(tree) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 2801bcae2..c4b2b2122 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -32,7 +32,7 @@ object Trees { /** Property key for trees with documentation strings attached */ val DocComment = new Property.Key[Comment] - @sharable private var nextId = 0 // for debugging + @sharable private var nextId = 0 // for debugging type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */ type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */ diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 639c4d111..a1b99d16d 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -216,8 +216,8 @@ object Contexts { else if (isNonEmptyScopeContext) scope.implicitDecls else Nil val outerImplicits = - if (isImportContext && importInfo.hiddenRoot.exists) - outer.implicits exclude importInfo.hiddenRoot + if (isImportContext && importInfo.unimported.exists) + outer.implicits exclude importInfo.unimported else outer.implicits if (implicitRefs.isEmpty) outerImplicits @@ -422,9 +422,18 @@ object Contexts { final def withOwner(owner: Symbol): Context = if (owner ne this.owner) fresh.setOwner(owner) else this - override def toString = + final def withProperty[T](key: Key[T], value: Option[T]): Context = + if (property(key) == value) this + else value match { + case Some(v) => fresh.setProperty(key, v) + case None => fresh.dropProperty(key) + } + + override def toString = { + def iinfo(implicit ctx: Context) = if (ctx.importInfo == null) "" else i"${ctx.importInfo.selectors}%, %" "Context(\n" + - (outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n") + (outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}, import = ${iinfo(ctx)}") mkString "\n") + } } /** A condensed context provides only a small memory footprint over @@ -468,6 +477,9 @@ object Contexts { def setProperty[T](key: Key[T], value: T): this.type = setMoreProperties(moreProperties.updated(key, value)) + def dropProperty(key: Key[_]): this.type = + setMoreProperties(moreProperties - key) + def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid)) def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end)) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4b090d9b1..e4e5761b2 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -12,13 +12,20 @@ import collection.mutable import scala.reflect.api.{ Universe => ApiUniverse } object Definitions { - val MaxTupleArity, MaxAbstractFunctionArity = 22 - val MaxFunctionArity = 30 - // Awaiting a definite solution that drops the limit altogether, 30 gives a safety - // margin over the previous 22, so that treecopiers in miniphases are allowed to - // temporarily create larger closures. This is needed in lambda lift where large closures - // are first formed by treecopiers before they are split apart into parameters and - // environment in the lambdalift transform itself. + + /** The maximum number of elements in a tuple or product. + * This should be removed once we go to hlists. + */ + val MaxTupleArity = 22 + + /** The maximum arity N of a function type that's implemented + * as a trait `scala.FunctionN`. Functions of higher arity are possible, + * but are mapped in erasure to functions taking a single parameter of type + * Object[]. + * The limit 22 is chosen for Scala2x interop. It could be something + * else without affecting the set of programs that can be compiled. + */ + val MaxImplementedFunctionArity = 22 } /** A class defining symbols and types of standard definitions @@ -45,32 +52,29 @@ class Definitions { ctx.newSymbol(owner, name, flags | Permanent, info) private def newClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, infoFn: ClassSymbol => Type) = - ctx.newClassSymbol(owner, name, flags | Permanent, infoFn).entered + ctx.newClassSymbol(owner, name, flags | Permanent, infoFn) - private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = + private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = - completeClass(newCompleteClassSymbol(ScalaPackageClass, name, flags, parents)) - - private def newTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = + private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) - private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - newTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) + private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = + enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) - private def newSyntheticTypeParam(cls: ClassSymbol, scope: MutableScope, paramFlags: FlagSet, suffix: String = "T0") = - newTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) + private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") = + enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only // implemented in Dotty and not in Scala 2. // See <http://docs.scala-lang.org/sips/pending/repeated-byname.html>. - private def specialPolyClass(name: TypeName, paramFlags: FlagSet, parentConstrs: => Seq[Type]): ClassSymbol = { + private def enterSpecialPolyClass(name: TypeName, paramFlags: FlagSet, parentConstrs: => Seq[Type]): ClassSymbol = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val paramDecls = newScope - val typeParam = newSyntheticTypeParam(cls, paramDecls, paramFlags) + val typeParam = enterSyntheticTypeParam(cls, paramFlags, paramDecls) def instantiate(tpe: Type) = if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) else tpe @@ -79,31 +83,54 @@ class Definitions { denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parentRefs, paramDecls) } } - newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer) + newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered + } + + /** The trait FunctionN, for some N */ + private def newFunctionNTrait(n: Int) = { + val completer = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + val cls = denot.asClass.classSymbol + val decls = newScope + val argParams = + for (i <- List.range(0, n)) yield + enterTypeParam(cls, s"T$i".toTypeName, Contravariant, decls) + val resParam = enterTypeParam(cls, s"R".toTypeName, Covariant, decls) + val applyMeth = + decls.enter( + newMethod(cls, nme.apply, + MethodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) + denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) + } + } + newClassSymbol(ScalaPackageClass, s"Function$n".toTypeName, Trait, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = - newSymbol(cls, name.encode, flags | Method, info).entered.asTerm + newSymbol(cls, name.encode, flags | Method, info).asTerm + + private def enterMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = + newMethod(cls, name, info, flags).entered - private def newAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = { + private def enterAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = { val sym = newSymbol(ScalaPackageClass, name, flags, TypeAlias(tpe)) ScalaPackageClass.currentPackageDecls.enter(sym) sym } - private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, + private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) val tparamBounds = tparamNames map (_ => TypeBounds.empty) val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn) - newMethod(cls, name, ptype, flags) + enterMethod(cls, name, ptype, flags) } - private def newT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, resultTypeFn, flags) + private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + enterPolyMethod(cls, name, 1, resultTypeFn, flags) - private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) + private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + enterPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = { val arr = new Array[TypeRef](arity + 1) @@ -172,20 +199,20 @@ class Definitions { * def getClass: java.lang.Class[T] = ??? * } */ - lazy val AnyClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) + lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) def AnyType = AnyClass.typeRef - lazy val AnyValClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) + lazy val AnyValClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) def AnyValType = AnyValClass.typeRef - lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) - lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) - lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) - lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) - lazy val Any_toString = newMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) - lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) - lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) - lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) - lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) + lazy val Any_== = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) + lazy val Any_!= = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) + lazy val Any_equals = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) + lazy val Any_hashCode = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) + lazy val Any_toString = enterMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) + lazy val Any_## = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) + lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) + lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) + lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) @@ -205,37 +232,37 @@ class Definitions { } def ObjectType = ObjectClass.typeRef - lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) + lazy val AnyRefAlias: TypeSymbol = enterAliasType(tpnme.AnyRef, ObjectType) def AnyRefType = AnyRefAlias.typeRef - lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) - lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) - lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, + lazy val Object_eq = enterMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) + lazy val Object_ne = enterMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) + lazy val Object_synchronized = enterPolyMethod(ObjectClass, nme.synchronized_, 1, pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) - lazy val Object_clone = newMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) - lazy val Object_finalize = newMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) - lazy val Object_notify = newMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) - lazy val Object_notifyAll = newMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) - lazy val Object_wait = newMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) - lazy val Object_waitL = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) - lazy val Object_waitLI = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) + lazy val Object_clone = enterMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) + lazy val Object_finalize = enterMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) + lazy val Object_notify = enterMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) + lazy val Object_notifyAll = enterMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) + lazy val Object_wait = enterMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) + lazy val Object_waitL = enterMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) + lazy val Object_waitLI = enterMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) /** Dummy method needed by elimByName */ - lazy val dummyApply = newPolyMethod( + lazy val dummyApply = enterPolyMethod( OpsPackageClass, nme.dummyApply, 1, pt => MethodType(List(FunctionOf(Nil, PolyParam(pt, 0))), PolyParam(pt, 0))) /** Method representing a throw */ - lazy val throwMethod = newMethod(OpsPackageClass, nme.THROWkw, + lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, MethodType(List(ThrowableType), NothingType)) - lazy val NothingClass: ClassSymbol = newCompleteClassSymbol( + lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol( ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef)) def NothingType = NothingClass.typeRef - lazy val NullClass: ClassSymbol = newCompleteClassSymbol( + lazy val NullClass: ClassSymbol = enterCompleteClassSymbol( ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef)) def NullType = NullClass.typeRef @@ -281,7 +308,7 @@ class Definitions { lazy val SingletonClass: ClassSymbol = // needed as a synthetic class because Scala 2.x refers to it in classfiles // but does not define it as an explicit class. - newCompleteClassSymbol( + enterCompleteClassSymbol( ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final, List(AnyClass.typeRef), EmptyScope) @@ -387,17 +414,17 @@ class Definitions { lazy val BoxedDoubleModule = ctx.requiredModule("java.lang.Double") lazy val BoxedUnitModule = ctx.requiredModule("java.lang.Void") - lazy val ByNameParamClass2x = specialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType)) - lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType)) + lazy val ByNameParamClass2x = enterSpecialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType)) + lazy val EqualsPatternClass = enterSpecialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType)) - lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType)) + lazy val RepeatedParamClass = enterSpecialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType)) // fundamental classes lazy val StringClass = ctx.requiredClass("java.lang.String") def StringType: Type = StringClass.typeRef lazy val StringModule = StringClass.linkedClass - lazy val String_+ = newMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) + lazy val String_+ = enterMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) lazy val String_valueOf_Object = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match { case List(pt) => (pt isRef AnyClass) || (pt isRef ObjectClass) case _ => false @@ -431,6 +458,9 @@ class Definitions { def PartialFunctionClass(implicit ctx: Context) = PartialFunctionType.symbol.asClass lazy val AbstractPartialFunctionType: TypeRef = ctx.requiredClassRef("scala.runtime.AbstractPartialFunction") def AbstractPartialFunctionClass(implicit ctx: Context) = AbstractPartialFunctionType.symbol.asClass + lazy val FunctionXXLType: TypeRef = ctx.requiredClassRef("scala.FunctionXXL") + def FunctionXXLClass(implicit ctx: Context) = FunctionXXLType.symbol.asClass + lazy val SymbolType: TypeRef = ctx.requiredClassRef("scala.Symbol") def SymbolClass(implicit ctx: Context) = SymbolType.symbol.asClass lazy val DynamicType: TypeRef = ctx.requiredClassRef("scala.Dynamic") @@ -562,14 +592,15 @@ class Definitions { object FunctionOf { def apply(args: List[Type], resultType: Type)(implicit ctx: Context) = FunctionType(args.length).appliedTo(args ::: resultType :: Nil) - def unapply(ft: Type)(implicit ctx: Context)/*: Option[(List[Type], Type)]*/ = { - // -language:keepUnions difference: unapply needs result type because inferred type - // is Some[(List[Type], Type)] | None, which is not a legal unapply type. + def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol - lazy val targs = ft.argInfos - val numArgs = targs.length - 1 - if (numArgs >= 0 && numArgs <= MaxFunctionArity && - (FunctionType(numArgs).symbol == tsym)) Some(targs.init, targs.last) + if (isFunctionClass(tsym)) { + lazy val targs = ft.argInfos + val numArgs = targs.length - 1 + if (numArgs >= 0 && FunctionType(numArgs).symbol == tsym) + Some(targs.init, targs.last) + else None + } else None } } @@ -612,19 +643,26 @@ class Definitions { // ----- Symbol sets --------------------------------------------------- - lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxAbstractFunctionArity, 0) + lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0) val AbstractFunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => AbstractFunctionType.map(_.symbol.asClass)) def AbstractFunctionClass(n: Int)(implicit ctx: Context) = AbstractFunctionClassPerRun()(ctx)(n) - lazy val FunctionType = mkArityArray("scala.Function", MaxFunctionArity, 0) - def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => FunctionType.map(_.symbol.asClass)) - def FunctionClass(n: Int)(implicit ctx: Context) = FunctionClassPerRun()(ctx)(n) - lazy val Function0_applyR = FunctionType(0).symbol.requiredMethodRef(nme.apply) - def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol + private lazy val ImplementedFunctionType = mkArityArray("scala.Function", MaxImplementedFunctionArity, 0) + def FunctionClassPerRun = new PerRun[Array[Symbol]](implicit ctx => ImplementedFunctionType.map(_.symbol.asClass)) lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2) lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0) - private lazy val FunctionTypes: Set[TypeRef] = FunctionType.toSet + def FunctionClass(n: Int)(implicit ctx: Context) = + if (n < MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n) + else ctx.requiredClass("scala.Function" + n.toString) + + lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply) + def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol + + def FunctionType(n: Int)(implicit ctx: Context): TypeRef = + if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n) + else FunctionClass(n).typeRef + private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet @@ -646,10 +684,19 @@ class Definitions { tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) + def isUnimplementedFunctionClass(cls: Symbol) = + isFunctionClass(cls) && cls.name.functionArity > MaxImplementedFunctionArity def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) + val predefClassNames: Set[Name] = + Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName) + + /** Is `cls` the predef module class, or a class inherited by Predef? */ + def isPredefClass(cls: Symbol) = + (cls.owner eq ScalaPackageClass) && predefClassNames.contains(cls.name) + val StaticRootImportFns = List[() => TermRef]( () => JavaLangPackageVal.termRef, () => ScalaPackageVal.termRef @@ -688,10 +735,11 @@ class Definitions { def isProductSubType(tp: Type)(implicit ctx: Context) = (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) - def isFunctionType(tp: Type)(implicit ctx: Context) = { - val arity = functionArity(tp) - 0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionType(arity).symbol) - } + def isFunctionType(tp: Type)(implicit ctx: Context) = + isFunctionClass(tp.dealias.typeSymbol) && { + val arity = functionArity(tp) + arity >= 0 && tp.isRef(FunctionType(functionArity(tp)).typeSymbol) + } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 @@ -767,6 +815,23 @@ class Definitions { // ----- Initialization --------------------------------------------------- + /** Give the scala package a scope where a FunctionN trait is automatically + * added when someone looks for it. + */ + private def makeScalaSpecial()(implicit ctx: Context) = { + val oldInfo = ScalaPackageClass.classInfo + val oldDecls = oldInfo.decls + val newDecls = new MutableScope(oldDecls) { + override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { + val res = super.lookupEntry(name) + if (res == null && name.functionArity > 0) + newScopeEntry(newFunctionNTrait(name.functionArity)) + else res + } + } + ScalaPackageClass.info = oldInfo.derivedClassInfo(decls = newDecls) + } + /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticScalaClasses = List( AnyClass, @@ -794,6 +859,8 @@ class Definitions { def init()(implicit ctx: Context) = { this.ctx = ctx if (!_isInitialized) { + makeScalaSpecial() + // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 6a39c5787..99c688d50 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -132,7 +132,7 @@ object Denotations { def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(implicit ctx: Context): Denotation /** The variant of this denotation that's current in the given context. - * If no such denotation exists, returns the denotation with each alternative + * If no such denotation exists, returns the denotation with each alternative * at its first point of definition. */ def current(implicit ctx: Context): Denotation @@ -744,6 +744,20 @@ object Denotations { else NoDenotation } + /** The next defined denotation (following `nextInRun`) or an arbitrary + * undefined denotation, if all denotations in a `nextinRun` cycle are + * undefined. + */ + private def nextDefined: SingleDenotation = { + var p1 = this + var p2 = nextInRun + while (p1.validFor == Nowhere && (p1 ne p2)) { + p1 = p1.nextInRun + p2 = p2.nextInRun.nextInRun + } + p1 + } + /** Produce a denotation that is valid for the given context. * Usually called when !(validFor contains ctx.period) * (even though this is not a precondition). @@ -763,8 +777,9 @@ object Denotations { // can happen if we sit on a stale denotation which has been replaced // wholesale by an installAfter; in this case, proceed to the next // denotation and try again. - if (validFor == Nowhere && nextInRun.validFor != Nowhere) return nextInRun.current - assert(false) + val nxt = nextDefined + if (nxt.validFor != Nowhere) return nxt + assert(false, this) } if (valid.runId != currentPeriod.runId) @@ -905,6 +920,7 @@ object Denotations { prev.nextInRun = this this.nextInRun = old.nextInRun old.validFor = Nowhere + old.nextInRun = this } def staleSymbolError(implicit ctx: Context) = { diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 4c7f5b0a9..7a4fc0512 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -229,6 +229,12 @@ object NameOps { } } + def functionArity: Int = + if (name.startsWith(tpnme.Function)) + try name.drop(tpnme.Function.length).toString.toInt + catch { case ex: NumberFormatException => -1 } + else -1 + /** The name of the generic runtime operation corresponding to an array operation */ def genericArrayOp: TermName = name match { case nme.apply => nme.array_apply diff --git a/compiler/src/dotty/tools/dotc/core/Periods.scala b/compiler/src/dotty/tools/dotc/core/Periods.scala index 6efadab7f..29d9d208f 100644 --- a/compiler/src/dotty/tools/dotc/core/Periods.scala +++ b/compiler/src/dotty/tools/dotc/core/Periods.scala @@ -153,7 +153,7 @@ object Periods { final val FirstPhaseId = 1 /** The number of bits needed to encode a phase identifier. */ - final val PhaseWidth = 6 + final val PhaseWidth = 7 final val PhaseMask = (1 << PhaseWidth) - 1 final val MaxPossiblePhaseId = PhaseMask } diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 222e2235d..6a53e1b30 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -26,7 +26,10 @@ trait Phases { def phasesStack: List[Phase] = if ((this eq NoContext) || !phase.exists) Nil - else phase :: outersIterator.dropWhile(_.phase == phase).next.phasesStack + else { + val rest = outersIterator.dropWhile(_.phase == phase) + phase :: (if (rest.hasNext) rest.next.phasesStack else Nil) + } /** Execute `op` at given phase */ def atPhase[T](phase: Phase)(op: Context => T): T = diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index 3daa8117e..6090079e5 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -309,7 +309,7 @@ object Scopes { /** Lookup a symbol entry matching given name. */ - override final def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { + override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { var e: ScopeEntry = null if (hashTable ne null) { e = hashTable(name.hashCode & (hashTable.length - 1)) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c2a14b36f..741ff8b1f 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -265,6 +265,7 @@ object StdNames { val THIS: N = "_$this" val TRAIT_CONSTRUCTOR: N = "$init$" val U2EVT: N = "u2evt$" + val ALLARGS: N = "$allArgs" final val Nil: N = "Nil" final val Predef: N = "Predef" diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index f78820fff..f8c0cdac9 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -260,6 +260,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { secondTry(tp1, tp2) } compareErasedValueType + case ConstantType(v2) => + tp1 match { + case ConstantType(v1) => v1.value == v2.value + case _ => secondTry(tp1, tp2) + } case ErrorType => true case _ => @@ -541,9 +546,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/ def comparePaths = tp2 match { case tp2: TermRef => - tp2.info.widenExpr match { + tp2.info.widenExpr.dealias match { case tp2i: SingletonType => - isSubType(tp1, tp2i) // see z1720.scala for a case where this can arise even in typer. + isSubType(tp1, tp2i) + // see z1720.scala for a case where this can arise even in typer. + // Also, i1753.scala, to show why the dealias above is necessary. case _ => false } case _ => diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 28dbed8f6..82943377a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -7,6 +7,7 @@ import Uniques.unique import dotc.transform.ExplicitOuter._ import dotc.transform.ValueClasses._ import util.DotClass +import Definitions.MaxImplementedFunctionArity /** Erased types are: * @@ -38,7 +39,10 @@ object TypeErasure { case _: ErasedValueType => true case tp: TypeRef => - tp.symbol.isClass && tp.symbol != defn.AnyClass && tp.symbol != defn.ArrayClass + val sym = tp.symbol + sym.isClass && + sym != defn.AnyClass && sym != defn.ArrayClass && + !defn.isUnimplementedFunctionClass(sym) case _: TermRef => true case JavaArrayType(elem) => @@ -176,8 +180,13 @@ object TypeErasure { else if (sym.isAbstractType) TypeAlias(WildcardType) else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) else erase.eraseInfo(tp, sym)(erasureCtx) match { - case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => - MethodType(Nil, defn.BoxedUnitType) + case einfo: MethodType => + if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass)) + MethodType(Nil, defn.BoxedUnitType) + else if (sym.isAnonymousFunction && einfo.paramTypes.length > MaxImplementedFunctionArity) + MethodType(nme.ALLARGS :: Nil, JavaArrayType(defn.ObjectType) :: Nil, einfo.resultType) + else + einfo case einfo => einfo } @@ -317,6 +326,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean * - For a term ref p.x, the type <noprefix> # x. * - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object| * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. + * - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL * - For a typeref P.C where C refers to a class, <noprefix> # C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. @@ -345,6 +355,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (!sym.isClass) this(tp.info) else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. + else if (defn.isUnimplementedFunctionClass(sym)) defn.FunctionXXLType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 92e5f9d57..39214dd0c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -197,6 +197,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case c :: rest => val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu if (cs == c.baseClasses) accu1 else dominators(rest, accu1) + case Nil => // this case can happen because after erasure we do not have a top class anymore + assert(ctx.erasedTypes) + defn.ObjectClass :: Nil } def mergeRefined(tp1: Type, tp2: Type): Type = { diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 89bc21929..7e6620f8e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2862,14 +2862,14 @@ object Types { * * @param origin The parameter that's tracked by the type variable. * @param creatorState The typer state in which the variable was created. - * @param owningTree The function part of the TypeApply tree tree that introduces - * the type variable. + * @param bindingTree The TypeTree which introduces the type variable, or EmptyTree + * if the type variable does not correspond to a source term. * @paran owner The current owner if the context where the variable was created. * * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated * at some given point. See `Inferencing#interpolateUndetVars`. */ - final class TypeVar(val origin: PolyParam, creatorState: TyperState, val owningTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { + final class TypeVar(val origin: PolyParam, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { /** The permanent instance type of the variable, or NoType is none is given yet */ private[core] var inst: Type = NoType diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index fa0576c7a..704f399ca 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -83,6 +83,15 @@ object Parsers { def atPos[T <: Positioned](start: Offset)(t: T): T = atPos(start, start)(t) + def startOffset(t: Positioned): Int = + if (t.pos.exists) t.pos.start else in.offset + + def pointOffset(t: Positioned): Int = + if (t.pos.exists) t.pos.point else in.offset + + def endOffset(t: Positioned): Int = + if (t.pos.exists) t.pos.end else in.lastOffset + def nameStart: Offset = if (in.token == BACKQUOTED_IDENT) in.offset + 1 else in.offset @@ -448,7 +457,7 @@ object Parsers { val topInfo = opStack.head opStack = opStack.tail val od = reduceStack(base, topInfo.operand, 0, true) - return atPos(od.pos.start, topInfo.offset) { + return atPos(startOffset(od), topInfo.offset) { PostfixOp(od, topInfo.operator) } } @@ -492,7 +501,7 @@ object Parsers { /** Accept identifier acting as a selector on given tree `t`. */ def selector(t: Tree): Tree = - atPos(t.pos.start, in.offset) { Select(t, ident()) } + atPos(startOffset(t), in.offset) { Select(t, ident()) } /** Selectors ::= ident { `.' ident() * @@ -588,7 +597,7 @@ object Parsers { val isNegated = negOffset < in.offset atPos(negOffset) { if (in.token == SYMBOLLIT) atPos(in.skipToken()) { SymbolLit(in.strVal) } - else if (in.token == INTERPOLATIONID) interpolatedString() + else if (in.token == INTERPOLATIONID) interpolatedString(inPattern) else finish(in.token match { case CHARLIT => in.charVal case INTLIT => in.intVal(isNegated).toInt @@ -612,10 +621,14 @@ object Parsers { in.nextToken() while (in.token == STRINGPART) { segmentBuf += Thicket( - literal(), + literal(inPattern = inPattern), atPos(in.offset) { if (in.token == IDENTIFIER) termIdent() + else if (in.token == USCORE && inPattern) { + in.nextToken() + Ident(nme.WILDCARD) + } else if (in.token == THIS) { in.nextToken() This(EmptyTypeIdent) @@ -624,12 +637,12 @@ object Parsers { if (inPattern) Block(Nil, inBraces(pattern())) else expr() else { - ctx.error(InterpolatedStringError()) + ctx.error(InterpolatedStringError(), source atPos Position(in.offset)) EmptyTree } }) } - if (in.token == STRINGLIT) segmentBuf += literal() + if (in.token == STRINGLIT) segmentBuf += literal(inPattern = inPattern) InterpolatedString(interpolator, segmentBuf.toList) } @@ -728,7 +741,7 @@ object Parsers { def refinedTypeRest(t: Tree): Tree = { newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) refinedTypeRest(atPos(t.pos.start) { RefinedTypeTree(t, refinement()) }) + if (in.token == LBRACE) refinedTypeRest(atPos(startOffset(t)) { RefinedTypeTree(t, refinement()) }) else t } @@ -749,7 +762,7 @@ object Parsers { def annotType(): Tree = annotTypeRest(simpleType()) def annotTypeRest(t: Tree): Tree = - if (in.token == AT) annotTypeRest(atPos(t.pos.start) { Annotated(t, annot()) }) + if (in.token == AT) annotTypeRest(atPos(startOffset(t)) { Annotated(t, annot()) }) else t /** SimpleType ::= SimpleType TypeArgs @@ -780,19 +793,19 @@ object Parsers { val handleSingletonType: Tree => Tree = t => if (in.token == TYPE) { in.nextToken() - atPos(t.pos.start) { SingletonTypeTree(t) } + atPos(startOffset(t)) { SingletonTypeTree(t) } } else t private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) - case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs(namedOK = true)) }) + case LBRACKET => simpleTypeRest(atPos(startOffset(t)) { AppliedTypeTree(t, typeArgs(namedOK = true)) }) case _ => t } private def typeProjection(t: Tree): Tree = { accept(HASH) val id = typeIdent() - atPos(t.pos.start, id.pos.start) { Select(t, id.name) } + atPos(startOffset(t), startOffset(id)) { Select(t, id.name) } } /** NamedTypeArg ::= id `=' Type @@ -846,7 +859,7 @@ object Parsers { val t = toplevelTyp() if (isIdent(nme.raw.STAR)) { in.nextToken() - atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) } + atPos(startOffset(t)) { PostfixOp(t, nme.raw.STAR) } } else t } @@ -971,7 +984,7 @@ object Parsers { val t = expr1(location) if (in.token == ARROW) { placeholderParams = saved - closureRest(t.pos.start, location, convertToParams(t)) + closureRest(startOffset(t), location, convertToParams(t)) } else if (isWildcard(t)) { placeholderParams = placeholderParams ::: saved @@ -1025,7 +1038,7 @@ object Parsers { assert(handlerStart != -1) syntaxError( new EmptyCatchBlock(body), - Position(handlerStart, handler.pos.end) + Position(handlerStart, endOffset(handler)) ) case _ => } @@ -1035,7 +1048,7 @@ object Parsers { else { if (handler.isEmpty) warning( EmptyCatchAndFinallyBlock(body), - source atPos Position(tryOffset, body.pos.end) + source atPos Position(tryOffset, endOffset(body)) ) EmptyTree } @@ -1057,21 +1070,21 @@ object Parsers { case EQUALS => t match { case Ident(_) | Select(_, _) | Apply(_, _) => - atPos(t.pos.start, in.skipToken()) { Assign(t, expr()) } + atPos(startOffset(t), in.skipToken()) { Assign(t, expr()) } case _ => t } case COLON => ascription(t, location) case MATCH => - atPos(t.pos.start, in.skipToken()) { + atPos(startOffset(t), in.skipToken()) { inBraces(Match(t, caseClauses())) } case _ => t } - def ascription(t: Tree, location: Location.Value) = atPos(t.pos.start, in.skipToken()) { + def ascription(t: Tree, location: Location.Value) = atPos(startOffset(t), in.skipToken()) { in.token match { case USCORE => val uscoreStart = in.skipToken() @@ -1105,7 +1118,7 @@ object Parsers { val id = termIdent() val paramExpr = if (location == Location.InBlock && in.token == COLON) - atPos(id.pos.start, in.skipToken()) { Typed(id, infixType()) } + atPos(startOffset(id), in.skipToken()) { Typed(id, infixType()) } else id closureRest(start, location, convertToParam(paramExpr, mods) :: Nil) @@ -1194,13 +1207,13 @@ object Parsers { in.nextToken() simpleExprRest(selector(t), canApply = true) case LBRACKET => - val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) } + val tapp = atPos(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true)) } simpleExprRest(tapp, canApply = true) case LPAREN | LBRACE if canApply => - val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) } + val app = atPos(startOffset(t), in.offset) { Apply(t, argumentExprs()) } simpleExprRest(app, canApply = true) case USCORE => - atPos(t.pos.start, in.skipToken()) { PostfixOp(t, nme.WILDCARD) } + atPos(startOffset(t), in.skipToken()) { PostfixOp(t, nme.WILDCARD) } case _ => t } @@ -1284,7 +1297,7 @@ object Parsers { if (in.token == IF) guard() else { val pat = pattern1() - if (in.token == EQUALS) atPos(pat.pos.start, in.skipToken()) { GenAlias(pat, expr()) } + if (in.token == EQUALS) atPos(startOffset(pat), in.skipToken()) { GenAlias(pat, expr()) } else generatorRest(pat) } @@ -1293,7 +1306,7 @@ object Parsers { def generator(): Tree = generatorRest(pattern1()) def generatorRest(pat: Tree) = - atPos(pat.pos.start, accept(LARROW)) { GenFrom(pat, expr()) } + atPos(startOffset(pat), accept(LARROW)) { GenFrom(pat, expr()) } /** ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}') * {nl} [`yield'] Expr @@ -1357,7 +1370,7 @@ object Parsers { val pattern = () => { val pat = pattern1() if (isIdent(nme.raw.BAR)) - atPos(pat.pos.start) { Alternative(pat :: patternAlts()) } + atPos(startOffset(pat)) { Alternative(pat :: patternAlts()) } else pat } @@ -1383,15 +1396,15 @@ object Parsers { // compatibility for Scala2 `x @ _*` syntax infixPattern() match { case pt @ Ident(tpnme.WILDCARD_STAR) => - migrationWarningOrError("The syntax `x @ _*' is no longer supported; use `x : _*' instead", p.pos.start) - atPos(p.pos.start, offset) { Typed(p, pt) } + migrationWarningOrError("The syntax `x @ _*' is no longer supported; use `x : _*' instead", startOffset(p)) + atPos(startOffset(p), offset) { Typed(p, pt) } case p => - atPos(p.pos.start, offset) { Bind(name, p) } + atPos(startOffset(p), offset) { Bind(name, p) } } case p @ Ident(tpnme.WILDCARD_STAR) => // compatibility for Scala2 `_*` syntax - migrationWarningOrError("The syntax `_*' is no longer supported; use `x : _*' instead", p.pos.start) - atPos(p.pos.start) { Typed(Ident(nme.WILDCARD), p) } + migrationWarningOrError("The syntax `_*' is no longer supported; use `x : _*' instead", startOffset(p)) + atPos(startOffset(p)) { Typed(Ident(nme.WILDCARD), p) } case p => p } @@ -1415,7 +1428,7 @@ object Parsers { val simplePattern = () => in.token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS => path(thisOK = true) match { - case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(id.pos.start) + case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id)) case t => simplePatternRest(t) } case USCORE => @@ -1435,7 +1448,7 @@ object Parsers { case XMLSTART => xmlLiteralPattern() case _ => - if (isLiteral) literal() + if (isLiteral) literal(inPattern = true) else { syntaxErrorOrIncomplete(IllegalStartOfSimplePattern()) errorTermTree @@ -1445,9 +1458,9 @@ object Parsers { def simplePatternRest(t: Tree): Tree = { var p = t if (in.token == LBRACKET) - p = atPos(t.pos.start, in.offset) { TypeApply(p, typeArgs()) } + p = atPos(startOffset(t), in.offset) { TypeApply(p, typeArgs()) } if (in.token == LPAREN) - p = atPos(t.pos.start, in.offset) { Apply(p, argumentPatterns()) } + p = atPos(startOffset(t), in.offset) { Apply(p, argumentPatterns()) } p } @@ -1573,7 +1586,8 @@ object Parsers { case Select(qual, name) => cpy.Select(tree)(adjustStart(start)(qual), name) case _ => tree } - if (start < tree1.pos.start) tree1.withPos(tree1.pos.withStart(start)) + if (tree1.pos.exists && start < tree1.pos.start) + tree1.withPos(tree1.pos.withStart(start)) else tree1 } @@ -1771,7 +1785,7 @@ object Parsers { case imp: Import => imp case sel @ Select(qual, name) => - val selector = atPos(sel.pos.point) { Ident(name) } + val selector = atPos(pointOffset(sel)) { Ident(name) } cpy.Import(sel)(qual, selector :: Nil) case t => accept(DOT) @@ -1804,7 +1818,7 @@ object Parsers { def importSelector(): Tree = { val from = termIdentOrWildcard() if (from.name != nme.WILDCARD && in.token == ARROW) - atPos(from.pos.start, in.skipToken()) { + atPos(startOffset(from), in.skipToken()) { Thicket(from, termIdentOrWildcard()) } else from @@ -2085,7 +2099,7 @@ object Parsers { /** Create a tree representing a packaging */ def makePackaging(start: Int, pkg: Tree, stats: List[Tree]): PackageDef = pkg match { - case x: RefTree => atPos(start, pkg.pos.point)(PackageDef(x, stats)) + case x: RefTree => atPos(start, pointOffset(pkg))(PackageDef(x, stats)) } /** Packaging ::= package QualId [nl] `{' TopStatSeq `}' diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 60003d098..101be167e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -758,7 +758,7 @@ object Scanners { finishStringPart() nextRawChar() next.token = LBRACE - } else if (Character.isUnicodeIdentifierStart(ch)) { + } else if (Character.isUnicodeIdentifierStart(ch) || ch == '_') { finishStringPart() do { putChar(ch) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 00627fc28..1ddf3cd6d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -614,14 +614,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { (sym.allOverriddenSymbols exists (_ is TypeParam)) override def toText(sym: Symbol): Text = { - if (sym.isImport) { - def importString(tree: untpd.Tree) = s"import ${tree.show}" + if (sym.isImport) sym.infoOrCompleter match { - case info: Namer#Completer => return importString(info.original) - case info: ImportType => return importString(info.expr) + case info: Namer#Completer => return info.original.show + case info: ImportType => return s"import $info.expr.show" case _ => } - } if (sym.is(ModuleClass)) kindString(sym) ~~ (nameString(sym.name.stripModuleClassSuffix) + idString(sym)) else diff --git a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala index 5b3669d5e..65c64f708 100644 --- a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala @@ -6,7 +6,7 @@ import java.io.{ File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream, ByteArrayOutputStream => ByteOutputStream } -import java.lang.{Class, ClassLoader} +import java.lang.{Class, ClassLoader, Thread, System, StringBuffer} import java.net.{URL, URLClassLoader} import scala.collection.immutable.ListSet @@ -443,7 +443,10 @@ class CompilingInterpreter( } // the types are all =>T; remove the => - val cleanedType = rawType.widenExpr + val cleanedType = rawType.widenExpr match { + case tp: MethodType => tp.resultType + case tp => tp + } map + (name -> ctx.atPhase(ctx.typerPhase.next) { implicit ctx => @@ -680,15 +683,49 @@ class CompilingInterpreter( code.print(resultExtractors.mkString("")) } + private val ListReg = """^.*List\[(\w+)\]$""".r + private val MapReg = """^.*Map\[(\w+),[ ]*(\w+)\]$""".r + private val LitReg = """^.*\((.+)\)$""".r + private def resultExtractor(req: Request, varName: Name): String = { val prettyName = varName.decode - val varType = string2code(req.typeOf(varName)) + // FIXME: `varType` is prettified to abbreviate common types where + // appropriate, and to also prettify literal types + // + // This should be rewritten to use the actual types once we have a + // semantic representation available to the REPL + val varType = string2code(req.typeOf(varName)) match { + // Extract List's paremeter from full path + case ListReg(param) => s"List[$param]" + // Extract Map's paremeters from full path + case MapReg(k, v) => s"Map[$k, $v]" + // Extract literal type from literal type representation. Example: + // + // ``` + // scala> val x: 42 = 42 + // val x: Int(42) = 42 + // scala> val y: "hello" = "hello" + // val y: String("hello") = "hello" + // ``` + case LitReg(lit) => lit + // When the type is a singleton value like None, don't show `None$` + // instead show `None.type`. + case x if x.lastOption == Some('$') => x.init + ".type" + case x => x + } val fullPath = req.fullPath(varName) - s""" + "$prettyName: $varType = " + { + val varOrVal = statement match { + case v: ValDef if v.mods is Flags.Mutable => "var" + case _ => "val" + } + + s""" + "$varOrVal $prettyName: $varType = " + { | if ($fullPath.asInstanceOf[AnyRef] != null) { - | (if ($fullPath.toString().contains('\\n')) "\\n" else "") + - | $fullPath.toString() + "\\n" + | (if ($fullPath.toString().contains('\\n')) "\\n" else "") + { + | import dotty.Show._ + | $fullPath.show /*toString()*/ + "\\n" + | } | } else { | "null\\n" | } @@ -735,9 +772,30 @@ class CompilingInterpreter( override def defNames = boundNames override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { - if (!defDef.mods.is(Flags.AccessFlags)) - code.print("+\"" + string2code(defDef.name.toString) + ": " + - string2code(req.typeOf(defDef.name)) + "\\n\"") + /** TODO: This is the result of the state of the REPL - this would be + * entirely unnecessary with a better structure where we could just + * use the type printer + * + * @see `def findTypes` for an explanation of what should be done + */ + if (!defDef.mods.is(Flags.AccessFlags)) { + // Take the DefDef and remove the `rhs` and ascribed type `tpt` + val copy = ast.untpd.cpy.DefDef(defDef)( + rhs = EmptyTree, + tpt = TypeTree + ) + + val tpt = defDef.tpt match { + // ascribed TypeExpr e.g: `def foo: Int = 5` + case Ident(tpt) if defDef.vparamss.isEmpty => + ": " + tpt.show + case tpt => + ": " + req.typeOf(defDef.name) + } + code.print { + "+\"" + string2code(copy.show) + tpt + "\\n\"" + } + } } } diff --git a/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala b/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala index b3ac41c55..cfe8d892d 100644 --- a/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala +++ b/compiler/src/dotty/tools/dotc/repl/InterpreterLoop.scala @@ -4,7 +4,7 @@ package repl import java.io.{BufferedReader, File, FileReader, PrintWriter} import java.io.IOException -import java.lang.{ClassLoader, System} +import java.lang.{ClassLoader, System, Thread} import scala.concurrent.{Future, Await} import scala.concurrent.duration.Duration import reporting.Reporter diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index 8477cfe28..26c1e5ebc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -286,11 +286,16 @@ abstract class Reporter extends interfaces.ReporterResult { } /** Should this diagnostic not be reported at all? */ - def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing) + def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = + ctx.mode.is(Mode.Printing) /** Does this reporter contain not yet reported errors or warnings? */ def hasPending: Boolean = false + /** If this reporter buffers messages, remove and return all buffered messages. */ + def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] = Nil + /** Issue all error messages in this reporter to next outer one, or make sure they are written. */ - def flush()(implicit ctx: Context): Unit = {} + def flush()(implicit ctx: Context): Unit = + removeBufferedMessages.foreach(ctx.reporter.report) } diff --git a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala index 586273c2e..34b109882 100644 --- a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala @@ -36,11 +36,9 @@ class StoreReporter(outer: Reporter) extends Reporter { } } - override def flush()(implicit ctx: Context) = - if (infos != null) { - infos.foreach(ctx.reporter.report(_)) - infos = null - } + override def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] = + if (infos != null) try infos.toList finally infos = null + else Nil override def errorsReported = hasErrors || outer.errorsReported } diff --git a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala index 714255962..331fce46a 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala @@ -9,7 +9,6 @@ import dotty.tools.dotc.core.Symbols.NoSymbol import scala.annotation.tailrec import dotty.tools.dotc.core._ import Symbols._ -import scala.Some import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.Context diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 069176111..5c880c7bd 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -13,6 +13,7 @@ import core.StdNames._ import core.NameOps._ import core.Decorators._ import core.Constants._ +import core.Definitions._ import typer.NoChecking import typer.ProtoTypes._ import typer.ErrorReporting._ @@ -36,9 +37,17 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => + def isCompacted(sym: Symbol) = + sym.isAnonymousFunction && { + sym.info(ctx.withPhase(ctx.phase.next)) match { + case MethodType(nme.ALLARGS :: Nil, _) => true + case _ => false + } + } + assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") if (ref.symbol eq defn.ObjectClass) { - // Aftre erasure, all former Any members are now Object members + // After erasure, all former Any members are now Object members val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info val extendedScope = decls.cloneScope for (decl <- defn.AnyClass.classInfo.decls) @@ -59,7 +68,10 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => val oldInfo = ref.info val newInfo = transformInfo(ref.symbol, oldInfo) val oldFlags = ref.flags - val newFlags = ref.flags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading + val newFlags = + if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param + else oldFlags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading + // TODO: define derivedSymDenotation? if ((oldSymbol eq newSymbol) && (oldOwner eq newOwner) && (oldInfo eq newInfo) && (oldFlags == newFlags)) ref else { @@ -82,7 +94,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => assertErased(tree) tree match { case res: tpd.This => - assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res), + assert(!ExplicitOuter.referencesOuter(ctx.owner.lexicallyEnclosingClass, res), i"Reference to $res from ${ctx.owner.showLocated}") case ret: tpd.Return => // checked only after erasure, as checking before erasure is complicated @@ -331,8 +343,23 @@ object Erasure extends TypeTestsCasts{ * e.m -> e.[]m if `m` is an array operation other than `clone`. */ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - val sym = tree.symbol - assert(sym.exists, tree.show) + + def mapOwner(sym: Symbol): Symbol = { + val owner = sym.owner + if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) { + assert(sym.isConstructor, s"${sym.showLocated}") + defn.ObjectClass + } + else if (defn.isUnimplementedFunctionClass(owner)) + defn.FunctionXXLClass + else + owner + } + + var sym = tree.symbol + val owner = mapOwner(sym) + if (owner ne sym.owner) sym = owner.info.decl(sym.name).symbol + assert(sym.exists, owner) def select(qual: Tree, sym: Symbol): Tree = { val name = tree.typeOpt match { @@ -366,11 +393,7 @@ object Erasure extends TypeTestsCasts{ def recur(qual: Tree): Tree = { val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType val symIsPrimitive = sym.owner.isPrimitiveValueClass - if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass)) { - assert(sym.isConstructor, s"${sym.showLocated}") - select(qual, defn.ObjectClass.info.decl(sym.name).symbol) - } - else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType) + if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType) recur(box(qual)) else if (!qualIsPrimitive && symIsPrimitive) recur(unbox(qual, sym.owner.typeRef)) @@ -389,7 +412,7 @@ object Erasure extends TypeTestsCasts{ } override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = - if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree) + if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree) else { ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}") outer.path(tree.symbol) @@ -423,6 +446,9 @@ object Erasure extends TypeTestsCasts{ } } + /** Besides normal typing, this method collects all arguments + * to a compacted function into a single argument of array type. + */ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree if (fun.symbol == defn.dummyApply) @@ -434,7 +460,13 @@ object Erasure extends TypeTestsCasts{ fun1.tpe.widen match { case mt: MethodType => val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased - val args1 = (outers ::: args ++ protoArgs(pt)).zipWithConserve(mt.paramTypes)(typedExpr) + var args0 = outers ::: args ++ protoArgs(pt) + if (args0.length > MaxImplementedFunctionArity && mt.paramTypes.length == 1) { + val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType)) + .withType(defn.ArrayOf(defn.ObjectType)) + args0 = bunchedArgs :: Nil + } + val args1 = args0.zipWithConserve(mt.paramTypes)(typedExpr) untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType case _ => throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") @@ -470,18 +502,36 @@ object Erasure extends TypeTestsCasts{ super.typedValDef(untpd.cpy.ValDef(vdef)( tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) + /** Besides normal typing, this function also compacts anonymous functions + * with more than `MaxImplementedFunctionArity` parameters to ise a single + * parameter of type `[]Object`. + */ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { val restpe = if (sym.isConstructor) defn.UnitType else sym.info.resultType + var vparamss1 = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil + var rhs1 = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs + } + if (sym.isAnonymousFunction && vparamss1.head.length > MaxImplementedFunctionArity) { + val bunchedParam = ctx.newSymbol(sym, nme.ALLARGS, Flags.TermParam, JavaArrayType(defn.ObjectType)) + def selector(n: Int) = ref(bunchedParam) + .select(defn.Array_apply) + .appliedTo(Literal(Constant(n))) + val paramDefs = vparamss1.head.zipWithIndex.map { + case (paramDef, idx) => + assignType(untpd.cpy.ValDef(paramDef)(rhs = selector(idx)), paramDef.symbol) + } + vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil + rhs1 = untpd.Block(paramDefs, rhs1) + } val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, - vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil, + vparamss = vparamss1, tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), - rhs = ddef.rhs match { - case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) - case _ => ddef.rhs - }) + rhs = rhs1) super.typedDefDef(ddef1, sym) } diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 3fec47e9f..a32e1c921 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -330,7 +330,7 @@ object ExplicitOuter { /** The path of outer accessors that references `toCls.this` starting from * the context owner's this node. */ - def path(toCls: Symbol, start: Tree = This(ctx.owner.enclosingClass.asClass)): Tree = try { + def path(toCls: Symbol, start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass)): Tree = try { def loop(tree: Tree): Tree = { val treeCls = tree.tpe.widen.classSymbol val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore diff --git a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala index 7c60e8d72..21ca8dbfd 100644 --- a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -96,14 +96,24 @@ class InterceptedMethods extends MiniPhaseTransform { s"that means the intercepted methods set doesn't match the code") tree } - lazy val Select(qual, _) = tree.fun + lazy val qual = tree.fun match { + case Select(qual, _) => qual + case ident @ Ident(_) => + ident.tpe match { + case TermRef(prefix: TermRef, _) => + tpd.ref(prefix) + case TermRef(prefix: ThisType, _) => + tpd.This(prefix.cls) + } + + } val Any_## = this.Any_## val Any_!= = defn.Any_!= val rewrite: Tree = tree.fun.symbol match { case Any_## => - poundPoundValue(qual) + poundPoundValue(qual) case Any_!= => - qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!) + qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!) /* /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // todo: this is needed to support value classes @@ -121,7 +131,7 @@ class InterceptedMethods extends MiniPhaseTransform { // we get a primitive form of _getClass trying to target a boxed value // so we need replace that method name with Object_getClass to get correct behavior. // See SI-5568. - qual.selectWithSig(defn.Any_getClass).appliedToNone + qual.selectWithSig(defn.Any_getClass).appliedToNone case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 01c240e3a..0314d4ec4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -47,7 +47,9 @@ import Decorators._ } tree match { case ddef: DefDef - if !ddef.symbol.is(Deferred) && ddef.rhs == EmptyTree => + if !ddef.symbol.is(Deferred) && + !ddef.symbol.isConstructor && // constructors bodies are added later at phase Constructors + ddef.rhs == EmptyTree => errorLackImplementation(ddef) case tdef: TypeDef if tdef.symbol.isClass && !tdef.symbol.is(Deferred) && tdef.rhs == EmptyTree => @@ -89,10 +91,10 @@ import Decorators._ } lazy val field = sym.field.orElse(newField).asTerm - + def adaptToField(tree: Tree) = if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen) - + if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { def skipBlocks(t: Tree): Tree = t match { diff --git a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala index 9571c387b..a72e10681 100644 --- a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -55,7 +55,9 @@ class ParamForwarding(thisTransformer: DenotTransformer) { stat match { case stat: ValDef => val sym = stat.symbol.asTerm - if (sym is (ParamAccessor, butNot = Mutable)) { + if (sym.is(ParamAccessor, butNot = Mutable) && !sym.info.isInstanceOf[ExprType]) { + // ElimByName gets confused with methods returning an ExprType, + // so avoid param forwarding if parameter is by name. See i1766.scala val idx = superArgs.indexWhere(_.symbol == sym) if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter val alias = inheritedAccessor(sym) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6c398cd72..11121e1f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -250,8 +250,37 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Splice new method reference into existing application */ def spliceMeth(meth: Tree, app: Tree): Tree = app match { - case Apply(fn, args) => Apply(spliceMeth(meth, fn), args) - case TypeApply(fn, targs) => TypeApply(spliceMeth(meth, fn), targs) + case Apply(fn, args) => + spliceMeth(meth, fn).appliedToArgs(args) + case TypeApply(fn, targs) => + // Note: It is important that the type arguments `targs` are passed in new trees + // instead of being spliced in literally. Otherwise, a type argument to a default + // method could be constructed as the definition site of the type variable for + // that default constructor. This would interpolate type variables too early, + // causing lots of tests (among them tasty_unpickleScala2) to fail. + // + // The test case is in i1757.scala. Here we have a variable `s` and a method `cpy` + // defined like this: + // + // var s + // def cpy[X](b: List[Int] = b): B[X] = new B[X](b) + // + // The call `s.cpy()` then gets expanded to + // + // { val $1$: B[Int] = this.s + // $1$.cpy[X']($1$.cpy$default$1[X'] + // } + // + // A type variable gets interpolated if it does not appear in the type + // of the current tree and the current tree contains the variable's "definition". + // Previously, the polymorphic function tree to which the variable was first added + // was taken as the variable's definition. But that fails here because that + // tree was `s.cpy` but got transformed into `$1$.cpy`. We now take the type argument + // [X'] of the variable as its definition tree, which is more robust. But then + // it's crucial that the type tree is not copied directly as argument to + // `cpy$default$1`. If it was, the variable `X'` would already be interpolated + // when typing the default argument, which is too early. + spliceMeth(meth, fn).appliedToTypes(targs.tpes) case _ => meth } @@ -333,7 +362,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val getter = findDefaultGetter(n + numArgs(normalizedFun)) if (getter.isEmpty) missingArg(n) else { - addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal) + addTyped(treeToArg(spliceMeth(getter withPos normalizedFun.pos, normalizedFun)), formal) matchArgs(args1, formals1, n + 1) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5e1d8622a..f5f7bdbaa 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -456,7 +456,7 @@ trait Checking { /** Check that Java statics and packages can only be used in selections. */ def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { - if (!proto.isInstanceOf[SelectionProto]) { + if (!proto.isInstanceOf[SelectionProto] && !proto.isInstanceOf[ApplyingProto]) { val sym = tree.tpe.termSymbol // The check is avoided inside Java compilation units because it always fails // on the singleton type Module.type. diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index c444631ae..cd374e32c 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -44,6 +44,12 @@ class FrontEnd extends Phase { typr.println("entered: " + unit.source) } + def enterAnnotations(implicit ctx: Context) = monitor("annotating") { + val unit = ctx.compilationUnit + ctx.typer.annotate(unit.untpdTree :: Nil) + typr.println("annotated: " + unit.source) + } + def typeCheck(implicit ctx: Context) = monitor("typechecking") { val unit = ctx.compilationUnit unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree) @@ -69,8 +75,9 @@ class FrontEnd extends Phase { } unitContexts foreach (parse(_)) record("parsedTrees", ast.Trees.ntrees) - unitContexts foreach (enterSyms(_)) - unitContexts foreach (typeCheck(_)) + unitContexts.foreach(enterSyms(_)) + unitContexts.foreach(enterAnnotations(_)) + unitContexts.foreach(typeCheck(_)) record("total trees after typer", ast.Trees.ntrees) unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f3dceea71..1a9a8f64c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -24,6 +24,7 @@ import Constants._ import Applications._ import ProtoTypes._ import ErrorReporting._ +import reporting.diagnostic.MessageContainer import Inferencing.fullyDefinedType import Trees._ import Hashable._ @@ -212,6 +213,8 @@ object Implicits { /** A "no matching implicit found" failure */ case object NoImplicitMatches extends SearchFailure + case object DivergingImplicit extends SearchFailure + /** A search failure that can show information about the cause */ abstract class ExplainedSearchFailure extends SearchFailure { protected def pt: Type @@ -233,9 +236,35 @@ object Implicits { "\n " + explanation } - class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { - def explanation(implicit ctx: Context): String = - em"${err.refStr(ref)} does not $qualify" + class NonMatchingImplicit(ref: TermRef, + val pt: Type, + val argument: tpd.Tree, + trail: List[MessageContainer]) extends ExplainedSearchFailure { + private val separator = "\n**** because ****\n" + + /** Replace repeated parts beginning with `separator` by ... */ + private def elideRepeated(str: String): String = { + val startIdx = str.indexOfSlice(separator) + val nextIdx = str.indexOfSlice(separator, startIdx + separator.length) + if (nextIdx < 0) str + else { + val prefix = str.take(startIdx) + val first = str.slice(startIdx, nextIdx) + var rest = str.drop(nextIdx) + if (rest.startsWith(first)) { + rest = rest.drop(first.length) + val dots = "\n\n ...\n" + if (!rest.startsWith(dots)) rest = dots ++ rest + } + prefix ++ first ++ rest + } + } + + def explanation(implicit ctx: Context): String = { + val headMsg = em"${err.refStr(ref)} does not $qualify" + val trailMsg = trail.map(mc => i"$separator ${mc.message}").mkString + elideRepeated(headMsg ++ trailMsg) + } } class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure { @@ -273,9 +302,10 @@ trait ImplicitRunInfo { self: RunInfo => * a type variable, we need the current context, the current * runinfo context does not do. */ - def implicitScope(tp: Type, liftingCtx: Context): OfTypeImplicits = { + def implicitScope(rootTp: Type, liftingCtx: Context): OfTypeImplicits = { val seen: mutable.Set[Type] = mutable.Set() + val incomplete: mutable.Set[Type] = mutable.Set() /** Replace every typeref that does not refer to a class by a conjunction of class types * that has the same implicit scope as the original typeref. The motivation for applying @@ -309,16 +339,23 @@ trait ImplicitRunInfo { self: RunInfo => } } - def iscopeRefs(tp: Type): TermRefSet = - if (seen contains tp) EmptyTermRefSet - else { - seen += tp - iscope(tp).companionRefs - } - // todo: compute implicits directly, without going via companionRefs? def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") { ctx.traceIndented(i"collectCompanions($tp)", implicits) { + + def iscopeRefs(t: Type): TermRefSet = implicitScopeCache.get(t) match { + case Some(is) => + is.companionRefs + case None => + if (seen contains t) { + incomplete += tp // all references to rootTo will be accounted for in `seen` so we return `EmptySet`. + EmptyTermRefSet // on the other hand, the refs of `tp` are now not accurate, so `tp` is marked incomplete. + } else { + seen += t + iscope(t).companionRefs + } + } + val comps = new TermRefSet tp match { case tp: NamedType => @@ -356,7 +393,8 @@ trait ImplicitRunInfo { self: RunInfo => * @param isLifted Type `tp` is the result of a `liftToClasses` application */ def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = { - def computeIScope(cacheResult: Boolean) = { + val canCache = Config.cacheImplicitScopes && tp.hash != NotCached + def computeIScope() = { val savedEphemeral = ctx.typerState.ephemeral ctx.typerState.ephemeral = false try { @@ -367,33 +405,23 @@ trait ImplicitRunInfo { self: RunInfo => else collectCompanions(tp) val result = new OfTypeImplicits(tp, refs)(ctx) - if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") - else if (cacheResult) implicitScopeCache(tp) = result + if (ctx.typerState.ephemeral) + record("ephemeral cache miss: implicitScope") + else if (canCache && + ((tp eq rootTp) || // first type traversed is always cached + !incomplete.contains(tp) && // other types are cached if they are not incomplete + result.companionRefs.forall( // and all their companion refs are cached + implicitScopeCache.contains))) + implicitScopeCache(tp) = result result } finally ctx.typerState.ephemeral |= savedEphemeral } - - if (tp.hash == NotCached || !Config.cacheImplicitScopes) - computeIScope(cacheResult = false) - else implicitScopeCache get tp match { - case Some(is) => is - case None => - // Implicit scopes are tricky to cache because of loops. For example - // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains - // the scope of A which contains the scope of B. We break the loop - // by returning EmptyTermRefSet in `collectCompanions` for types - // that we have already seen, but this means that we cannot cache - // the computed scope of A, it is incomplete. - // Keeping track of exactly where these loops happen would require a - // lot of book-keeping, instead we choose to be conservative and only - // cache scopes before any type has been seen. This is unfortunate - // because loops are very common for types in scala.collection. - computeIScope(cacheResult = seen.isEmpty) - } + if (canCache) implicitScopeCache.getOrElse(tp, computeIScope()) + else computeIScope() } - iscope(tp) + iscope(rootTp) } /** A map that counts the number of times an implicit ref was picked */ @@ -587,7 +615,7 @@ trait Implicits { self: Typer => val wildProto = implicitProto(pt, wildApprox(_)) /** Search failures; overridden in ExplainedImplicitSearch */ - protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches + protected def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]): SearchFailure = NoImplicitMatches protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches protected def failedSearch: SearchFailure = NoImplicitMatches @@ -628,7 +656,7 @@ trait Implicits { self: Typer => { implicits.println(i"invalid eqAny[$tp1, $tp2]"); false } } if (ctx.reporter.hasErrors) - nonMatchingImplicit(ref) + nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages) else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && !shadowing.tpe.isError && !refMatches(shadowing)) { implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") @@ -637,7 +665,7 @@ trait Implicits { self: Typer => else generated1 match { case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil)) if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) => - nonMatchingImplicit(ref) + nonMatchingImplicit(ref, Nil) case _ => SearchSuccess(generated1, ref, ctx.typerState) } @@ -743,8 +771,8 @@ trait Implicits { self: Typer => fail } def failures = myFailures.toList - override def nonMatchingImplicit(ref: TermRef) = - record(new NonMatchingImplicit(ref, pt, argument)) + override def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]) = + record(new NonMatchingImplicit(ref, pt, argument, trail)) override def divergingImplicit(ref: TermRef) = record(new DivergingImplicit(ref, pt, argument)) override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 3aa289181..b4ec3390e 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -95,14 +95,22 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImp /** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden. * Note: this computation needs to work even for un-initialized import infos, and * is not allowed to force initialization. + * + * TODO: Once we have fully bootstrapped, I would prefer if we expressed + * unimport with an `override` modifier, and generalized it to all imports. + * I believe this would be more transparent than the curren set of conditions. E.g. + * + * override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String + + * override import java.lang.{} // disables all imports */ - lazy val hiddenRoot: Symbol = { - val sym = site.termSymbol - def hasMaskingSelector = selectors exists { + lazy val unimported: Symbol = { + lazy val sym = site.termSymbol + val hasMaskingSelector = selectors exists { case Thicket(_ :: Ident(nme.WILDCARD) :: Nil) => true case _ => false } - if ((defn.RootImportTypes exists (_.symbol == sym)) && hasMaskingSelector) sym else NoSymbol + if (hasMaskingSelector && defn.RootImportTypes.exists(_.symbol == sym)) sym + else NoSymbol } override def toString = { diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index aede4974a..1cb86dd72 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -216,10 +216,10 @@ object Inferencing { def interpolateUndetVars(tree: Tree, ownedBy: Symbol)(implicit ctx: Context): Unit = { val constraint = ctx.typerState.constraint val qualifies = (tvar: TypeVar) => - (tree contains tvar.owningTree) || ownedBy.exists && tvar.owner == ownedBy + (tree contains tvar.bindingTree) || ownedBy.exists && tvar.owner == ownedBy def interpolate() = Stats.track("interpolateUndetVars") { val tp = tree.tpe.widen - constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.owningTree.pos}")}") + constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.bindingTree.pos}")}") constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}") val vs = variances(tp, qualifies) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 148cf1da7..b8fe46745 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -528,18 +528,96 @@ class Namer { typer: Typer => } } - stats foreach expand + stats.foreach(expand) mergeCompanionDefs() val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx)) createCompanionLinks(ctxWithStats) ctxWithStats } + /** Add all annotations of definitions in `stats` to the defined symbols */ + def annotate(stats: List[Tree])(implicit ctx: Context): Unit = { + def recur(stat: Tree): Unit = stat match { + case pcl: PackageDef => + annotate(pcl.stats) + case stat: untpd.MemberDef => + stat.getAttachment(SymOfTree) match { + case Some(sym) => + sym.infoOrCompleter match { + case info: Completer if !defn.isPredefClass(sym.owner) => + // Annotate Predef methods only when they are completed; + // This is necessary to break a cyclic dependence between `Predef` + // and `deprecated` in test `compileStdLib`. + addAnnotations(sym, stat)(info.creationContext) + case _ => + // Annotations were already added as part of the symbol's completion + } + case none => + assert(stat.typeOpt.exists, i"no symbol for $stat") + } + case stat: untpd.Thicket => + stat.trees.foreach(recur) + case _ => + } + + for (stat <- stats) recur(expanded(stat)) + } + + /** Add annotations of `stat` to `sym`. + * This method can be called twice on a symbol (e.g. once + * during the `annotate` phase and then again during completion). + * Therefore, care needs to be taken not to add annotations again + * that are already added to the symbol. + */ + def addAnnotations(sym: Symbol, stat: MemberDef)(implicit ctx: Context) = { + // (1) The context in which an annotation of a top-level class or module is evaluated + // is the closest enclosing context which has the enclosing package as owner. + // (2) The context in which an annotation for any other symbol is evaluated is the + // closest enclosing context which has the owner of the class enclosing the symbol as owner. + // E.g in + // + // package p + // import a.b + // class C { + // import d.e + // @ann m() ... + // } + // + // `@ann` is evaluated in the context just outside `C`, where the `a.b` + // import is visible but the `d.e` import is forgotten. This measure is necessary + // in order to avoid cycles. + lazy val annotCtx = { + var target = sym.owner.lexicallyEnclosingClass + if (!target.is(PackageClass)) target = target.owner + var c = ctx + while (c.owner != target) c = c.outer + c + } + for (annotTree <- untpd.modsDeco(stat).mods.annotations) { + val cls = typedAheadAnnotation(annotTree)(annotCtx) + if (sym.unforcedAnnotation(cls).isEmpty) { + val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) + sym.addAnnotation(ann) + if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor)) + sym.setFlag(Inline) + } + } + } + + def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = { + val localCtx = index(stats) + annotate(stats) + localCtx + } + /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ class Completer(val original: Tree)(implicit ctx: Context) extends LazyType { protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) + /** The context with which this completer was created */ + def creationContext = ctx + protected def typeSig(sym: Symbol): Type = original match { case original: ValDef => if (sym is Module) moduleValSig(sym) @@ -572,19 +650,6 @@ class Namer { typer: Typer => completeInCreationContext(denot) } - protected def addAnnotations(denot: SymDenotation): Unit = original match { - case original: untpd.MemberDef => - var hasInlineAnnot = false - for (annotTree <- untpd.modsDeco(original).mods.annotations) { - val cls = typedAheadAnnotation(annotTree) - val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) - denot.addAnnotation(ann) - if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor)) - denot.setFlag(Inline) - } - case _ => - } - private def addInlineInfo(denot: SymDenotation) = original match { case original: untpd.DefDef if denot.isInlineMethod => Inliner.registerInlineInfo( @@ -598,7 +663,10 @@ class Namer { typer: Typer => * to pick up the context at the point where the completer was created. */ def completeInCreationContext(denot: SymDenotation): Unit = { - addAnnotations(denot) + original match { + case original: MemberDef => addAnnotations(denot.symbol, original) + case _ => + } addInlineInfo(denot) denot.info = typeSig(denot.symbol) Checking.checkWellFormed(denot.symbol) @@ -742,7 +810,7 @@ class Namer { typer: Typer => ok } - addAnnotations(denot) + addAnnotations(denot.symbol, original) val selfInfo = if (self.isEmpty) NoType @@ -765,9 +833,10 @@ class Namer { typer: Typer => // accessors, that's why the constructor needs to be completed before // the parent types are elaborated. index(constr) + annotate(constr :: params) symbolOfTree(constr).ensureCompleted() - index(rest)(inClassContext(selfInfo)) + indexAndAnnotate(rest)(inClassContext(selfInfo)) val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors))) @@ -790,7 +859,7 @@ class Namer { typer: Typer => case Some(ttree) => ttree case none => val ttree = typer.typed(tree, pt) - xtree.pushAttachment(TypedAhead, ttree) + xtree.putAttachment(TypedAhead, ttree) ttree } } @@ -810,7 +879,7 @@ class Namer { typer: Typer => /** Enter and typecheck parameter list */ def completeParams(params: List[MemberDef])(implicit ctx: Context) = { - index(params) + indexAndAnnotate(params) for (param <- params) typedAheadExpr(param) } @@ -990,7 +1059,7 @@ class Namer { typer: Typer => // 3. Info of CP is computed (to be copied to DP). // 4. CP is completed. // 5. Info of CP is copied to DP and DP is completed. - index(tparams) + indexAndAnnotate(tparams) if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted()) for (tparam <- tparams) typedAheadExpr(tparam) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9a20a452e..ed6b95c3b 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -353,20 +353,23 @@ object ProtoTypes { * 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]) = { + def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeTree]) = { val state = ctx.typerState assert(!(ctx.typerState.isCommittable && owningTree.isEmpty), s"inconsistent: no typevars were added to committable constraint ${state.constraint}") - def newTypeVars(pt: PolyType): List[TypeVar] = + def newTypeVars(pt: PolyType): List[TypeTree] = for (n <- (0 until pt.paramNames.length).toList) - yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner) + yield { + val tt = new TypeTree().withPos(owningTree.pos) + tt.withType(new TypeVar(PolyParam(pt, n), state, tt, ctx.owner)) + } val added = if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType) else pt val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) - ctx.typeComparer.addToConstraint(added, tvars) + ctx.typeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]]) (added, tvars) } diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 2413c0c22..3252ead47 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -72,9 +72,10 @@ class ReTyper extends Typer { override def localTyper(sym: Symbol) = this override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx + override def annotate(trees: List[untpd.Tree])(implicit ctx: Context) = () - override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = - fallBack(tree, ctx.typerState) + override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: => Tree)(implicit ctx: Context): Tree = + fallBack override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = () diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bc335299b..78c479433 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -27,7 +27,7 @@ import EtaExpansion.etaExpand import dotty.tools.dotc.transform.Erasure.Boxing import util.Positions._ import util.common._ -import util.SourcePosition +import util.{SourcePosition, Property} import collection.mutable import annotation.tailrec import Implicits._ @@ -57,6 +57,8 @@ object Typer { def assertPositioned(tree: untpd.Tree)(implicit ctx: Context) = if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}") + + private val ExprOwner = new Property.Key[Symbol] } class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking with Docstrings { @@ -74,7 +76,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * Note: It would be more proper to move importedFromRoot into typedIdent. * We should check that this has no performance degradation, however. */ - private var importedFromRoot: Set[Symbol] = Set() + private var unimported: Set[Symbol] = Set() /** Temporary data item for single call to typed ident: * This symbol would be found under Scala2 mode, but is not @@ -102,15 +104,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ def error(msg: => Message, pos: Position) = ctx.error(msg, pos) - /** Is this import a root import that has been shadowed by an explicit - * import in the same program? - */ - def isDisabled(imp: ImportInfo, site: Type): Boolean = { - if (imp.isRootImport && (importedFromRoot contains site.termSymbol)) return true - if (imp.hiddenRoot.exists) importedFromRoot += imp.hiddenRoot - false - } - /** Does this identifier appear as a constructor of a pattern? */ def isPatternConstr = if (ctx.mode.isExpr && (ctx.outer.mode is Mode.Pattern)) @@ -188,32 +181,44 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit /** The type representing a named import with enclosing name when imported * from given `site` and `selectors`. */ - def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = { - def checkUnambiguous(found: Type) = { - val other = namedImportRef(site, selectors.tail) - if (other.exists && found.exists && (found != other)) - error(em"reference to `$name` is ambiguous; it is imported twice in ${ctx.tree}", - tree.pos) - found - } + def namedImportRef(imp: ImportInfo)(implicit ctx: Context): Type = { val Name = name.toTermName.decode - selectors match { + def recur(selectors: List[untpd.Tree]): Type = selectors match { case selector :: rest => + def checkUnambiguous(found: Type) = { + val other = recur(selectors.tail) + if (other.exists && found.exists && (found != other)) + error(em"reference to `$name` is ambiguous; it is imported twice in ${ctx.tree}", + tree.pos) + found + } + + def selection(name: Name) = + if (imp.sym.isCompleting) { + ctx.warning(i"cyclic ${imp.sym}, ignored", tree.pos) + NoType + } + else if (unimported.nonEmpty && unimported.contains(imp.site.termSymbol)) + NoType + else { + // Pass refctx so that any errors are reported in the context of the + // reference instead of the + checkUnambiguous(selectionType(imp.site, name, tree.pos)(refctx)) + } + selector match { case Thicket(fromId :: Ident(Name) :: _) => val Ident(from) = fromId - val selName = if (name.isTypeName) from.toTypeName else from - // Pass refctx so that any errors are reported in the context of the - // reference instead of the context of the import. - checkUnambiguous(selectionType(site, selName, tree.pos)(refctx)) + selection(if (name.isTypeName) from.toTypeName else from) case Ident(Name) => - checkUnambiguous(selectionType(site, name, tree.pos)(refctx)) + selection(name) case _ => - namedImportRef(site, rest) + recur(rest) } case nil => NoType } + recur(imp.selectors) } /** The type representing a wildcard import with enclosing name when imported @@ -222,7 +227,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = { if (imp.isWildcardImport) { val pre = imp.site - if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) { + if (!unimported.contains(pre.termSymbol) && + !imp.excluded.contains(name.toTermName) && + name != nme.CONSTRUCTOR) { val denot = pre.member(name).accessibleFrom(pre)(refctx) if (reallyExists(denot)) return pre.select(name, denot) } @@ -279,19 +286,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (result.exists) result else { // find import val curImport = ctx.importInfo + def updateUnimported() = + if (curImport.unimported.exists) unimported += curImport.unimported if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists) previous // no more conflicts possible in this case - else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) { - val namedImp = namedImportRef(curImport.site, curImport.selectors) + else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo)) { + val namedImp = namedImportRef(curImport) if (namedImp.exists) findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer) - else if (isPossibleImport(wildImport)) { + else if (isPossibleImport(wildImport) && !curImport.sym.isCompleting) { val wildImp = wildImportRef(curImport) if (wildImp.exists) findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer) - else loop(outer) + else { + updateUnimported() + loop(outer) + } + } + else { + updateUnimported() + loop(outer) } - else loop(outer) } else loop(outer) } @@ -311,11 +326,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit return typed(desugar.patternVar(tree), pt) } - val rawType = { - val saved1 = importedFromRoot + val saved1 = unimported val saved2 = foundUnderScala2 - importedFromRoot = Set.empty + unimported = Set.empty foundUnderScala2 = NoType try { var found = findRef(NoType, BindingPrec.nothingBound, NoContext) @@ -329,7 +343,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit found } finally { - importedFromRoot = saved1 + unimported = saved1 foundUnderScala2 = saved2 } } @@ -576,7 +590,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) = - (index(stats), typedStats(stats, ctx.owner)) + (indexAndAnnotate(stats), typedStats(stats, ctx.owner)) def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { val (exprCtx, stats1) = typedBlockStats(tree.stats) @@ -1058,7 +1072,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") { val PolyTypeTree(tparams, body) = tree - index(tparams) + indexAndAnnotate(tparams) val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) val body1 = typedType(tree.body) assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1) @@ -1121,7 +1135,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = { // necessary to force annotation trees to be computed. sym.annotations.foreach(_.ensureCompleted) - val annotCtx = ctx.outersIterator.dropWhile(_.owner == sym).next + lazy val annotCtx = { + val c = ctx.outersIterator.dropWhile(_.owner == sym).next + c.property(ExprOwner) match { + case Some(exprOwner) if c.owner.isClass => + // We need to evaluate annotation arguments in an expression context, since + // classes defined in a such arguments should not be entered into the + // enclosing class. + c.exprContext(mdef, exprOwner) + case None => c + } + } // necessary in order to mark the typed ahead annotations as definitely typed: untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx)) } @@ -1544,7 +1568,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case nil => buf.toList } - traverse(stats) + val localCtx = { + val exprOwnerOpt = if (exprOwner == ctx.owner) None else Some(exprOwner) + ctx.withProperty(ExprOwner, exprOwnerOpt) + } + traverse(stats)(localCtx) } /** Given an inline method `mdef`, the method rewritten so that its body @@ -1589,18 +1617,34 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * `fallBack`. * * 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`. + * This strategy is not tried if the prototype represents already + * another `.apply` or `.apply()` selection. * 2nd strategy: If tree is a select `qual.name`, try to insert an implicit conversion * around the qualifier part `qual` so that the result conforms to the expected type * with wildcard result type. */ - def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = - tryEither { implicit ctx => + def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: => Tree)(implicit ctx: Context): Tree = { + + /** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */ + def isApplyProto(pt: Type): Boolean = pt match { + case pt: SelectionProto => pt.name == nme.apply + case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType) + case pt: IgnoredProto => isApplyProto(pt.ignored) + case _ => false + } + + def tryApply(implicit ctx: Context) = { val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) if (sel.tpe.isError) sel else adapt(sel, pt) - } { (failedTree, failedState) => - tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack(failedTree, failedState)) } + def tryImplicit = + tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack) + + if (isApplyProto(pt)) tryImplicit + else tryEither(tryApply(_))((_, _) => tryImplicit) + } + /** If this tree is a select node `qual.name`, try to insert an implicit conversion * `c` around `qual` so that `c(qual).name` conforms to `pt`. */ @@ -1692,7 +1736,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil pt match { case pt: FunProto => - tryInsertApplyOrImplicit(tree, pt)((_, _) => noMatches) + tryInsertApplyOrImplicit(tree, pt)(noMatches) case _ => if (altDenots exists (_.info.paramTypess == ListOfNil)) typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt) @@ -1731,7 +1775,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Apply(_, _) => " more" case _ => "" } - (_, _) => errorTree(tree, em"$methodStr does not take$more parameters") + errorTree(tree, em"$methodStr does not take$more parameters") } } @@ -1834,7 +1878,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx.typeComparer.GADTused = false if (ctx.mode is Mode.Pattern) { tree match { - case _: RefTree | _: Literal if !isVarPattern(tree) => + case _: RefTree | _: Literal + if !isVarPattern(tree) && + !(tree.tpe <:< pt)(ctx.addMode(Mode.GADTflexible)) => checkCanEqual(pt, wtp, tree.pos)(ctx.retractMode(Mode.Pattern)) case _ => } @@ -1936,21 +1982,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (pt.isInstanceOf[PolyProto]) tree else { var typeArgs = tree match { - case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo + case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree) case _ => Nil } if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 convertNewGenericArray( - adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) + adaptInterpolated(tree.appliedToTypeTrees(typeArgs), pt, original)) } case wtp => pt match { case pt: FunProto => adaptToArgs(wtp, pt) case pt: PolyProto => - tryInsertApplyOrImplicit(tree, pt) { - (_, _) => tree // error will be reported in typedTypeApply - } + tryInsertApplyOrImplicit(tree, pt)(tree) // error will be reported in typedTypeApply case _ => if (ctx.mode is Mode.Type) adaptType(tree.tpe) else adaptNoArgs(wtp) diff --git a/compiler/src/dotty/tools/dotc/util/Chars.scala b/compiler/src/dotty/tools/dotc/util/Chars.scala index bae3b4732..6f95b87c4 100644 --- a/compiler/src/dotty/tools/dotc/util/Chars.scala +++ b/compiler/src/dotty/tools/dotc/util/Chars.scala @@ -6,7 +6,6 @@ package dotty.tools.dotc package util import scala.annotation.switch -import java.lang.{ Character => JCharacter } import java.lang.{Character => JCharacter} import java.lang.Character.LETTER_NUMBER import java.lang.Character.LOWERCASE_LETTER @@ -66,16 +65,16 @@ object Chars { /** Can character start an alphanumeric Scala identifier? */ def isIdentifierStart(c: Char): Boolean = - (c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c) + (c == '_') || (c == '$') || JCharacter.isUnicodeIdentifierStart(c) /** Can character form part of an alphanumeric Scala identifier? */ def isIdentifierPart(c: Char) = - (c == '$') || Character.isUnicodeIdentifierPart(c) + (c == '$') || JCharacter.isUnicodeIdentifierPart(c) /** Is character a math or other symbol in Unicode? */ def isSpecial(c: Char) = { - val chtp = Character.getType(c) - chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt + val chtp = JCharacter.getType(c) + chtp == JCharacter.MATH_SYMBOL.toInt || chtp == JCharacter.OTHER_SYMBOL.toInt } private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_' |