From e837e2a0a816bed4f532a4232ae8e96307b9a172 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Oct 2014 16:25:26 +0200 Subject: [PATCH 01/18] Add the right constructor to Java annotations --- src/dotty/tools/dotc/core/Flags.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotc/core/pickling/ClassfileParser.scala | 40 +++++++++++++++++++ tests/pos/annot.scala | 11 +++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/pos/annot.scala diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e5bf27eaeb00..e0c188965d4f 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -318,7 +318,7 @@ object Flags { /** An unpickled Scala 2.x class */ final val Scala2x = typeFlag(26, "") - /** A method that has default params */ // TODO: drop + /** A method that has default params */ final val DefaultParameterized = termFlag(27, "") /** Symbol is initialized to the default value, e.g. var x: T = _ */ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..b16446034479 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1992,7 +1992,7 @@ object Types { if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (restpe eq this.resultType)) this else copy(paramNames, paramBounds, restpe) - def copy(paramNames: List[TypeName], paramBounds: List[TypeBounds], restpe: Type)(implicit ctx: Context) = + def copy(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, restpe: Type)(implicit ctx: Context) = PolyType(paramNames)( x => paramBounds mapConserve (_.subst(this, x).bounds), x => restpe.subst(this, x)) diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 67f8255025f1..73389c6d11aa 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -128,6 +128,7 @@ class ClassfileParser( for (i <- 0 until in.nextChar) parseMember(method = false) for (i <- 0 until in.nextChar) parseMember(method = true) classInfo = parseAttributes(classRoot.symbol, classInfo) + if (isAnnotation) addAnnotationConstructor(classInfo) setClassInfo(classRoot, classInfo) setClassInfo(moduleRoot, staticInfo) } @@ -546,6 +547,45 @@ class ClassfileParser( newType } + /** Add a synthetic constructor and potentially also default getters which + * reflects the fields of the annotation with given `classInfo`. + * Annotations in Scala are assumed to get all their arguments as constructor + * parameters. For Java annotations we need to fake it by making up the constructor. + * Note that default getters have type Nothing. That's OK because we need + * them only to signal that the corresponding parameter is optional. + */ + def addAnnotationConstructor(classInfo: Type, tparams: List[Symbol] = Nil)(implicit ctx: Context): Unit = { + def addDefaultGetter(attr: Symbol, n: Int) = + ctx.newSymbol( + owner = moduleRoot.symbol, + name = nme.CONSTRUCTOR.defaultGetterName(n), + flags = attr.flags & Flags.AccessFlags, + info = defn.NothingType).entered + + classInfo match { + case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => + addAnnotationConstructor(restpe, tparams) + case classInfo: TempClassInfoType => + val attrs = classInfo.decls.toList.filter(_.isTerm) + val targs = tparams.map(_.typeRef) + val methType = MethodType( + attrs.map(_.name.asTermName), + attrs.map(_.info.resultType), + classRoot.typeRef.appliedTo(targs)) + val constr = ctx.newSymbol( + owner = classRoot.symbol, + name = nme.CONSTRUCTOR, + flags = Flags.Synthetic, + info = if (tparams.isEmpty) methType else TempPolyType(tparams, methType) + ).entered + for ((attr, i) <- attrs.zipWithIndex) + if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { + constr.setFlag(Flags.HasDefaultParams) + addDefaultGetter(attr, i) + } + } + } + /** Enter own inner classes in the right scope. It needs the scopes to be set up, * and implicitly current class' superclasses. */ diff --git a/tests/pos/annot.scala b/tests/pos/annot.scala new file mode 100644 index 000000000000..cda24e05daaf --- /dev/null +++ b/tests/pos/annot.scala @@ -0,0 +1,11 @@ +import java.beans.Transient + +class Test { + + @SuppressWarnings(Array("hi")) def foo() = ??? + + @Transient(false) def bar = ??? + + @Transient() def baz = ??? +} + From 3dc0a8f7eac217d450838f7cd3cb05b43ccb45ac Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Sat, 11 Oct 2014 12:10:37 +0200 Subject: [PATCH 02/18] Testcase that shows bug in ElimRepeated. --- tests/pos/repeatedArgs.scala | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/pos/repeatedArgs.scala diff --git a/tests/pos/repeatedArgs.scala b/tests/pos/repeatedArgs.scala new file mode 100644 index 000000000000..f9abb4aee70e --- /dev/null +++ b/tests/pos/repeatedArgs.scala @@ -0,0 +1,3 @@ +object testRepeated { + def foo = java.lang.System.out.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d") +} From 34fa82cc3f0e4895b99128385a9708b14e8cf285 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Sat, 11 Oct 2014 12:21:12 +0200 Subject: [PATCH 03/18] Fix elimRepeated not transforming annotations. --- .../tools/dotc/transform/ElimRepeated.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index eb44c3e2cdc6..33fb796fbe33 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -11,6 +11,7 @@ import Contexts.Context import Symbols._ import Denotations._, SymDenotations._ import Decorators.StringInterpolators +import dotty.tools.dotc.core.Annotations.ConcreteAnnotation import scala.collection.mutable import DenotTransformers._ import Names.Name @@ -25,6 +26,34 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransfo override def phaseName = "elimRepeated" + object annotTransformer extends TreeMap { + override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform(tree) match { + case x @(_: Ident|_ :Select|_: Apply| _: TypeApply| _: DefDef) => transformTypeOfTree(x) + case x => x + } + } + + /** + * Overriden to solve a particular problem with not being eliminated inside annotation trees + * Dmitry: this should solve problem for now, + * following YAGNI principle I am convinced that we shouldn't make a solution + * for a generalized problem(transforming annotations trees) + * that manifests itself only here. + */ + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { + val info1 = transformInfo(ref.info, ref.symbol) + + ref match { + case ref: SymDenotation => + val annotTrees = ref.annotations.map(_.tree) + val annotTrees1 = annotTrees.mapConserve(annotTransformer.transform) + val annots1 = if(annotTrees eq annotTrees1) ref.annotations else annotTrees1.map(new ConcreteAnnotation(_)) + if ((info1 eq ref.info) && (annots1 eq ref.annotations)) ref + else ref.copySymDenotation(info = info1, annotations = annots1) + case _ => if (info1 eq ref.info) ref else ref.derivedSingleDenotation(ref.symbol, info1) + } + } + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = elimRepeated(tp) From 7fd8b1e0563edeb7ae05759058849c9be2896bc1 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 29 Oct 2014 17:22:17 +0100 Subject: [PATCH 04/18] Add Id's to types. Helps to track where erroneous type was created. --- src/dotty/tools/dotc/core/Types.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index b16446034479..e0a81c0d29a8 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -35,6 +35,8 @@ object Types { private var recCount = 0 // used temporarily for debugging. TODO: remove + private var nextId = 0 + /** The class of types. * The principal subclasses and sub-objects are as follows: * @@ -70,6 +72,13 @@ object Types { // ----- Tests ----------------------------------------------------- + val uniqId = { + nextId = nextId + 1 +// if(nextId == 19555) +// println("foo") + nextId + } + /** Is this type different from NoType? */ def exists: Boolean = true From 34f25b10a569b941f6b16ffe18631e17d327f271 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 29 Oct 2014 17:23:01 +0100 Subject: [PATCH 05/18] Fix typeAssigner ignoring existence of JavaSeqLiteral --- src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- src/dotty/tools/dotc/typer/TypeAssigner.scala | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 4c21fcf49229..07e8f8af5da6 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -126,8 +126,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { if (tpe derivesFrom defn.SeqClass) SeqLiteral(elems) else JavaSeqLiteral(elems) def JavaSeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral = - new untpd.JavaSeqLiteral(elems) - .withType(defn.ArrayClass.typeRef.appliedTo(ctx.typeComparer.lub(elems.tpes))) + ta.assignType(new untpd.JavaSeqLiteral(elems), elems) + def TypeTree(original: Tree)(implicit ctx: Context): TypeTree = TypeTree(original.tpe, original) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index cb6fefab1472..6db8b36b5db4 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -321,11 +321,14 @@ trait TypeAssigner { def assignType(tree: untpd.Throw)(implicit ctx: Context) = tree.withType(defn.NothingType) - def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = { - val ownType = - if (ctx.erasedTypes) defn.SeqType - else defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes).widen) - tree.withType(ownType) + def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = tree match { + case tree: JavaSeqLiteral => + tree.withType(defn.ArrayClass.typeRef.appliedTo(ctx.typeComparer.lub(elems.tpes))) + case _ => + val ownType = + if (ctx.erasedTypes) defn.SeqType + else defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes).widen) + tree.withType(ownType) } def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = From f06f3eda7d67cc92f300746efcae31b46ef78ca3 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 29 Oct 2014 17:25:28 +0100 Subject: [PATCH 06/18] Fix TypeErasure.sigName erasing java repeated params to Seq --- src/dotty/tools/dotc/TypeErasure.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 851be76583bf..0b65f369c3dd 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -95,7 +95,9 @@ object TypeErasure { def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = { val normTp = - if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + if (tp.isRepeatedParam) + if (isJava) tp.translateParameterized(defn.RepeatedParamClass, defn.ArrayClass) + else tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) else tp (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx) } From 2c757629a6cb94e745a91c2c107ad84769ee046e Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 29 Oct 2014 17:26:13 +0100 Subject: [PATCH 07/18] Fix underlyingIfRepeated always assuming Scala repeated. --- src/dotty/tools/dotc/core/TypeApplications.scala | 7 +++++-- src/dotty/tools/dotc/transform/ElimRepeated.scala | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index bf756facfa28..3808cb17c55c 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -283,9 +283,12 @@ class TypeApplications(val self: Type) extends AnyVal { /** If this is repeated parameter type, its underlying Seq type, * or, if isJava is true, Array type, else the type itself. */ - def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type = - if (self.isRepeatedParam) translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type = { + if (self.isRepeatedParam) + if (isJava) translateParameterized(defn.RepeatedParamClass, defn.ArrayClass) + else translateParameterized(defn.RepeatedParamClass, defn.SeqClass) else self + } /** If this is an encoding of a (partially) applied type, return its arguments, * otherwise return Nil. diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index 33fb796fbe33..731d64e7a731 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -61,9 +61,10 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransfo case tp @ MethodType(paramNames, paramTypes) => val resultType1 = elimRepeated(tp.resultType) val paramTypes1 = - if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) - paramTypes.init :+ paramTypes.last.underlyingIfRepeated(tp.isJava) - else paramTypes + if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { + val last = paramTypes.last.underlyingIfRepeated(tp.isJava) + paramTypes.init :+ last + } else paramTypes tp.derivedMethodType(paramNames, paramTypes1, resultType1) case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimRepeated(tp.resultType)) From b15bd30b97d4c92f1b441bbaac13c0bed27f72aa Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 15:50:03 +0100 Subject: [PATCH 08/18] FunProtoTyped to be used when args are known to be typed --- src/dotty/tools/dotc/typer/ProtoTypes.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 0aa0aa53814f..917c195fd1b9 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -227,6 +227,16 @@ object ProtoTypes { override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer) } + + /** A prototype for expressions that appear in function position + * + * [](args): resultType, where args are known to be typed + */ + class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) { + override def typedArgs = args + override def argsAreTyped = true + } + /** A prototype for implicitly inferred views: * * []: argType => resultType From 033be6411b88b4870fd085d7580ad61c446f601c Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 15:50:57 +0100 Subject: [PATCH 09/18] readAnnotationContents that should be able to resolve overloaded constructors. --- .../tools/dotc/core/pickling/UnPickler.scala | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 3510462cc329..ae39a6533f7b 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -9,8 +9,9 @@ import java.lang.Double.longBitsToDouble import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ +import dotty.tools.dotc.typer.ProtoTypes.{FunProtoTyped, FunProto} import util.Positions._ -import ast.Trees, ast.tpd._, ast.untpd +import dotty.tools.dotc.ast.{tpd, Trees, untpd}, ast.tpd._ import printing.Texts._ import printing.Printer import io.AbstractFile @@ -814,19 +815,42 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: * readAnnotation, readSymbolAnnotation, or readAnnotInfoArg */ protected def readAnnotationContents(end: Int)(implicit ctx: Context): Tree = { - val atp = readTypeRef() - val args = new ListBuffer[Tree] - while (readIndex != end) { - val argref = readNat() - args += { - if (isNameEntry(argref)) { - val name = at(argref, readName) - val arg = readClassfileAnnotArg(readNat()) - NamedArg(name.asTermName, arg) - } else readAnnotArg(argref) + ctx.atPhase(ctx.typerPhase) { implicit ctx => // needed to enable implicit search + // and fix circullar dependency between annotation.currect invoking + // elimrepeated that reads the same annotation + + val atp = readTypeRef() + val args = { + val t = new ListBuffer[Tree] + + while (readIndex != end) { + val argref = readNat() + t += { + if (isNameEntry(argref)) { + val name = at(argref, readName) + val arg = readClassfileAnnotArg(readNat()) + NamedArg(name.asTermName, arg) + } else readAnnotArg(argref) + } + } + t.toList } + println(atp) + val typer = ctx.typer + val proto = new FunProtoTyped(args, atp, typer) + val alts = atp.member(nme.CONSTRUCTOR).alternatives.map(_.termRef) + + val constructors = ctx.typer.resolveOverloaded(alts, proto) + assert(constructors.size == 1) // this is parsed from bytecode tree. there's nothing user can do about it + + val constr = constructors.head + val targs = atp.argTypes + val fun = tpd.New(atp withoutArgs targs) + .select(TermRef.withSig(atp.normalizedPrefix, constr.termSymbol.asTerm)) + .appliedToTypes(targs) + val apply = untpd.Apply(fun, args) + new typer.ApplyToTyped(apply, fun, constr, args, atp).result // needed to handle varargs } - New(atp, args.toList) } /** Read an annotation and as a side effect store it into From 5fcf5f8f50bb9beb1772131cca319aa32ec041e5 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 16:13:03 +0100 Subject: [PATCH 10/18] Allow resolving overloads without inferring views. --- src/dotty/tools/dotc/typer/Applications.scala | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 035f19028d7c..c1a414911162 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -388,6 +388,14 @@ trait Applications extends Compatibility { self: Typer => def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) } + /** Subclass of Application for applicability tests with type arguments and value + * argument trees. + */ + class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) { + override def addArg(arg: TypedArg, formal: Type) = + ok = ok & (argType(arg, formal) <:< formal) + } + /** Subclass of Application for applicability tests with value argument types. */ class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context) extends TestApplication(methRef, methRef, args, resultType) { @@ -754,6 +762,14 @@ trait Applications extends Compatibility { self: Typer => new ApplicableToTrees(methRef, targs, args, resultType)(nestedContext).success } + /** Is given method reference applicable to type arguments `targs` and argument trees `args` without invfering views? + * @param resultType The expected result type of the application + */ + def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = { + val nestedContext = ctx.fresh.setExploreTyperState + new ApplicableToTreesDirectly(methRef, targs, args, resultType)(nestedContext).success + } + /** Is given method reference applicable to argument types `args`? * @param resultType The expected result type of the application */ @@ -886,7 +902,7 @@ trait Applications extends Compatibility { self: Typer => * to form the method type. * todo: use techniques like for implicits to pick candidates quickly? */ - def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type] = Nil)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type] = Nil, resolveImplicits: Boolean = true)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty @@ -943,7 +959,10 @@ trait Applications extends Compatibility { self: Typer => alts def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = - alts filter (isApplicable(_, targs, args, resultType)) + alts filter ( alt => + if (resolveImplicits) isApplicable(alt, targs, args, resultType) + else isDirectlyApplicable(alt, targs, args, resultType) + ) val alts1 = narrowBySize(alts) if (isDetermined(alts1)) alts1 From f46c977ec9b63670669efc1e690d73df9c9f5f98 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 16:13:51 +0100 Subject: [PATCH 11/18] Fix StackOveflow due to implicit resolution in readAnnotationContents --- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index ae39a6533f7b..c491436c4388 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -840,7 +840,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val proto = new FunProtoTyped(args, atp, typer) val alts = atp.member(nme.CONSTRUCTOR).alternatives.map(_.termRef) - val constructors = ctx.typer.resolveOverloaded(alts, proto) + val constructors = ctx.typer.resolveOverloaded(alts, proto, Nil, false) assert(constructors.size == 1) // this is parsed from bytecode tree. there's nothing user can do about it val constr = constructors.head From 748b8f4dad205b1d7bee5b22a97fe22fd41b1005 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 16:20:57 +0100 Subject: [PATCH 12/18] Infer if overloading resolution should trigger implicit search. This doesn't require additional argument. Decision can be made solely from the phaseId. --- .../tools/dotc/core/pickling/UnPickler.scala | 61 +++++++++---------- src/dotty/tools/dotc/typer/Applications.scala | 4 +- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index c491436c4388..5e1021b1ccb9 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -815,42 +815,37 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: * readAnnotation, readSymbolAnnotation, or readAnnotInfoArg */ protected def readAnnotationContents(end: Int)(implicit ctx: Context): Tree = { - ctx.atPhase(ctx.typerPhase) { implicit ctx => // needed to enable implicit search - // and fix circullar dependency between annotation.currect invoking - // elimrepeated that reads the same annotation - - val atp = readTypeRef() - val args = { - val t = new ListBuffer[Tree] - - while (readIndex != end) { - val argref = readNat() - t += { - if (isNameEntry(argref)) { - val name = at(argref, readName) - val arg = readClassfileAnnotArg(readNat()) - NamedArg(name.asTermName, arg) - } else readAnnotArg(argref) - } + val atp = readTypeRef() + val args = { + val t = new ListBuffer[Tree] + + while (readIndex != end) { + val argref = readNat() + t += { + if (isNameEntry(argref)) { + val name = at(argref, readName) + val arg = readClassfileAnnotArg(readNat()) + NamedArg(name.asTermName, arg) + } else readAnnotArg(argref) } - t.toList } - println(atp) - val typer = ctx.typer - val proto = new FunProtoTyped(args, atp, typer) - val alts = atp.member(nme.CONSTRUCTOR).alternatives.map(_.termRef) - - val constructors = ctx.typer.resolveOverloaded(alts, proto, Nil, false) - assert(constructors.size == 1) // this is parsed from bytecode tree. there's nothing user can do about it - - val constr = constructors.head - val targs = atp.argTypes - val fun = tpd.New(atp withoutArgs targs) - .select(TermRef.withSig(atp.normalizedPrefix, constr.termSymbol.asTerm)) - .appliedToTypes(targs) - val apply = untpd.Apply(fun, args) - new typer.ApplyToTyped(apply, fun, constr, args, atp).result // needed to handle varargs + t.toList } + println(atp) + val typer = ctx.typer + val proto = new FunProtoTyped(args, atp, typer) + val alts = atp.member(nme.CONSTRUCTOR).alternatives.map(_.termRef) + + val constructors = ctx.typer.resolveOverloaded(alts, proto, Nil) + assert(constructors.size == 1) // this is parsed from bytecode tree. there's nothing user can do about it + + val constr = constructors.head + val targs = atp.argTypes + val fun = tpd.New(atp withoutArgs targs) + .select(TermRef.withSig(atp.normalizedPrefix, constr.termSymbol.asTerm)) + .appliedToTypes(targs) + val apply = untpd.Apply(fun, args) + new typer.ApplyToTyped(apply, fun, constr, args, atp).result // needed to handle varargs } /** Read an annotation and as a side effect store it into diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c1a414911162..6f9a70ebc4fc 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -902,7 +902,7 @@ trait Applications extends Compatibility { self: Typer => * to form the method type. * todo: use techniques like for implicits to pick candidates quickly? */ - def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type] = Nil, resolveImplicits: Boolean = true)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type] = Nil)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty @@ -960,7 +960,7 @@ trait Applications extends Compatibility { self: Typer => def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = alts filter ( alt => - if (resolveImplicits) isApplicable(alt, targs, args, resultType) + if (!ctx.isAfterTyper) isApplicable(alt, targs, args, resultType) else isDirectlyApplicable(alt, targs, args, resultType) ) From 9ce8c79d9d851f080386e7f4bb273cbea0a27710 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 30 Oct 2014 16:21:16 +0100 Subject: [PATCH 13/18] Dotty typer deviation triggered in Unpickler --- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 5e1021b1ccb9..7771ca76e519 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -845,7 +845,8 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: .select(TermRef.withSig(atp.normalizedPrefix, constr.termSymbol.asTerm)) .appliedToTypes(targs) val apply = untpd.Apply(fun, args) - new typer.ApplyToTyped(apply, fun, constr, args, atp).result // needed to handle varargs + new typer.ApplyToTyped(apply, fun, constr, args, atp).result.asInstanceOf[tpd.Tree] // needed to handle varargs + // Dotty deviation, for scalac the last cast wouldn't be required } /** Read an annotation and as a side effect store it into From b90f530d0989075f5379364c88a23d4222b84593 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 3 Nov 2014 11:55:51 +0100 Subject: [PATCH 14/18] Fix ElimRepeated not transforming modifiers in tree. --- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 +- src/dotty/tools/dotc/transform/ElimRepeated.scala | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 7771ca76e519..6855e23a064d 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -831,7 +831,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: } t.toList } - println(atp) + // println(atp) val typer = ctx.typer val proto = new FunProtoTyped(args, atp, typer) val alts = atp.member(nme.CONSTRUCTOR).alternatives.map(_.termRef) diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index 731d64e7a731..10bffb5e559d 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -92,10 +92,13 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransfo override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { assert(ctx.phase == thisTransformer) def overridesJava = tree.symbol.allOverriddenSymbols.exists(_ is JavaDefined) + val newAnnots = tree.mods.annotations.mapConserve(annotTransformer.transform) + val newTree = if (newAnnots eq tree.mods.annotations) tree + else cpy.DefDef(tree)(mods = Modifiers(tree.mods.flags, tree.mods.privateWithin, newAnnots)) if (tree.symbol.info.isVarArgsMethod && overridesJava) - addVarArgsBridge(tree)(ctx.withPhase(thisTransformer.next)) + addVarArgsBridge(newTree)(ctx.withPhase(thisTransformer.next)) else - tree + newTree } /** Add a Java varargs bridge From 95a71dee6e3dd8b630d8d4385fa43fc4535aa5be Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 3 Nov 2014 13:51:57 +0100 Subject: [PATCH 15/18] Rename PolyType.copy method. It clashes with generated one. See #209 for explanation. --- src/dotty/tools/dotc/core/Types.scala | 4 ++-- src/dotty/tools/dotc/typer/ProtoTypes.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e0a81c0d29a8..ccbbadf8b33b 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1999,9 +1999,9 @@ object Types { def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], restpe: Type)(implicit ctx: Context) = if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (restpe eq this.resultType)) this - else copy(paramNames, paramBounds, restpe) + else duplicate(paramNames, paramBounds, restpe) - def copy(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, restpe: Type)(implicit ctx: Context) = + def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, restpe: Type)(implicit ctx: Context) = PolyType(paramNames)( x => paramBounds mapConserve (_.subst(this, x).bounds), x => restpe.subst(this, x)) diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 917c195fd1b9..8d29916fa71d 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -321,7 +321,7 @@ object ProtoTypes { yield new TypeVar(PolyParam(pt, n), state, owningTree) val added = - if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) + if (state.constraint contains pt) pt.duplicate(pt.paramNames, pt.paramBounds, pt.resultType) else pt val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) state.constraint = state.constraint.add(added, tvars) From f2362829c2840b73e61708515b3128d8325f9de5 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 3 Nov 2014 14:28:17 +0100 Subject: [PATCH 16/18] #204 check for global uniqueness of definitions. Check now fails after erasure. Should become error after fixed. --- .../tools/dotc/transform/TreeChecker.scala | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 4a7d280e5344..3812e0e97005 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -65,19 +65,28 @@ class TreeChecker { class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { - val definedSyms = new mutable.HashSet[Symbol] - - def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = { - if (tree.isDef) { - assert(!definedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree") - definedSyms += tree.symbol + val nowDefinedSyms = new mutable.HashSet[Symbol] + val everDefinedSyms = new mutable.HashMap[Symbol, Tree] + + def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { + case tree: DefTree => + val sym = tree.symbol + everDefinedSyms.get(sym) match { + case Some(t) => + if(t ne tree) + ctx.warning(i"symbol ${tree.symbol} is defined at least twice in different parts of AST") + // should become an error + case None => + everDefinedSyms(sym) = tree + } + assert(!nowDefinedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree") + nowDefinedSyms += tree.symbol //println(i"defined: ${tree.symbol}") val res = op - definedSyms -= tree.symbol + nowDefinedSyms -= tree.symbol //println(i"undefined: ${tree.symbol}") res - } - else op + case _ => op } def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = @@ -88,7 +97,7 @@ class TreeChecker { def assertDefined(tree: untpd.Tree)(implicit ctx: Context) = if (tree.symbol.maybeOwner.isTerm) - assert(definedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}") + assert(nowDefinedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}") override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { val res = tree match { From 7a57075bcf54252513c7094ea047532d97a60bd2 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 3 Nov 2014 14:42:55 +0100 Subject: [PATCH 17/18] Allow checking that trees and their defined symbols have modifiers in sync. --- src/dotty/tools/dotc/config/ScalaSettings.scala | 1 + src/dotty/tools/dotc/transform/TreeChecker.scala | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 743925e40753..4b04b324cbaf 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -99,6 +99,7 @@ class ScalaSettings extends Settings.SettingGroup { val Yhelp = BooleanSetting("-Y", "Print a synopsis of private options.") val browse = PhasesSetting("-Ybrowse", "Browse the abstract syntax tree after") val Ycheck = PhasesSetting("-Ycheck", "Check the tree at the end of") + val YcheckMods = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync") val YcheckTypedTrees = BooleanSetting("-YcheckTypedTrees", "Check all constructured typed trees for type correctness") val Yshow = PhasesSetting("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") val Xcloselim = BooleanSetting("-Yclosure-elim", "Perform closure elimination.") diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 3812e0e97005..7eec1719b391 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -74,12 +74,23 @@ class TreeChecker { everDefinedSyms.get(sym) match { case Some(t) => if(t ne tree) - ctx.warning(i"symbol ${tree.symbol} is defined at least twice in different parts of AST") + ctx.warning(i"symbol ${sym} is defined at least twice in different parts of AST") // should become an error case None => everDefinedSyms(sym) = tree } - assert(!nowDefinedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree") + assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym} in $tree") + + if(ctx.settings.YcheckMods.value) { + tree match { + case t: MemberDef => + if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") + if (sym.flags != t.mods.flags) ctx.warning(s"symbol ${sym.fullName} flags ${sym.flags} doesn't match AST definition flags ${t.mods.flags}") + // todo: compare trees inside annotations + case _ => + } + } + nowDefinedSyms += tree.symbol //println(i"defined: ${tree.symbol}") val res = op From ceb9c9aa9010da177dae6fc34b6d225038c70bce Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 3 Nov 2014 14:52:01 +0100 Subject: [PATCH 18/18] Show full name of doubly defined symbols --- src/dotty/tools/dotc/transform/TreeChecker.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 7eec1719b391..92af5559a844 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -74,12 +74,12 @@ class TreeChecker { everDefinedSyms.get(sym) match { case Some(t) => if(t ne tree) - ctx.warning(i"symbol ${sym} is defined at least twice in different parts of AST") + ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") // should become an error case None => everDefinedSyms(sym) = tree } - assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym} in $tree") + assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree") if(ctx.settings.YcheckMods.value) { tree match {