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) } 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/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/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/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/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..ccbbadf8b33b 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 @@ -1990,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], paramBounds: List[TypeBounds], 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/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/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 3510462cc329..6855e23a064d 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 @@ -815,18 +816,37 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: */ 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) + 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 } - New(atp, args.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.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 diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index eb44c3e2cdc6..10bffb5e559d 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) @@ -32,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)) @@ -62,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 diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 4a7d280e5344..92af5559a844 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -65,19 +65,39 @@ class TreeChecker { class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { - val definedSyms = new mutable.HashSet[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 ${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.fullName} 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 _ => + } + } - 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 + 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 +108,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 { diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 035f19028d7c..6f9a70ebc4fc 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 */ @@ -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 (!ctx.isAfterTyper) isApplicable(alt, targs, args, resultType) + else isDirectlyApplicable(alt, targs, args, resultType) + ) val alts1 = narrowBySize(alts) if (isDetermined(alts1)) alts1 diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 0aa0aa53814f..8d29916fa71d 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 @@ -311,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) 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) = 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 = ??? +} + 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") +}