Skip to content

Annotations and java seq #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e837e2a
Add the right constructor to Java annotations
odersky Oct 10, 2014
3dc0a8f
Testcase that shows bug in ElimRepeated.
DarkDimius Oct 11, 2014
34fa82c
Fix elimRepeated not transforming annotations.
DarkDimius Oct 11, 2014
7fd8b1e
Add Id's to types.
DarkDimius Oct 29, 2014
34f25b1
Fix typeAssigner ignoring existence of JavaSeqLiteral
DarkDimius Oct 29, 2014
f06f3ed
Fix TypeErasure.sigName erasing java repeated params to Seq
DarkDimius Oct 29, 2014
2c75762
Fix underlyingIfRepeated always assuming Scala repeated.
DarkDimius Oct 29, 2014
b15bd30
FunProtoTyped to be used when args are known to be typed
DarkDimius Oct 30, 2014
033be64
readAnnotationContents that should be able to resolve overloaded cons…
DarkDimius Oct 30, 2014
5fcf5f8
Allow resolving overloads without inferring views.
DarkDimius Oct 30, 2014
f46c977
Fix StackOveflow due to implicit resolution in readAnnotationContents
DarkDimius Oct 30, 2014
748b8f4
Infer if overloading resolution should trigger implicit search.
DarkDimius Oct 30, 2014
9ce8c79
Dotty typer deviation triggered in Unpickler
DarkDimius Oct 30, 2014
b90f530
Fix ElimRepeated not transforming modifiers in tree.
DarkDimius Nov 3, 2014
95a71de
Rename PolyType.copy method. It clashes with generated one.
DarkDimius Nov 3, 2014
f236282
#204 check for global uniqueness of definitions.
DarkDimius Nov 3, 2014
7a57075
Allow checking that trees and their defined symbols have modifiers in…
DarkDimius Nov 3, 2014
ceb9c9a
Show full name of doubly defined symbols
DarkDimius Nov 3, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/dotty/tools/dotc/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ object Flags {
/** An unpickled Scala 2.x class */
final val Scala2x = typeFlag(26, "<scala-2.x>")

/** A method that has default params */ // TODO: drop
/** A method that has default params */
final val DefaultParameterized = termFlag(27, "<defaultparam>")

/** Symbol is initialized to the default value, e.g. var x: T = _ */
Expand Down
7 changes: 5 additions & 2 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 11 additions & 2 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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))
Expand Down
40 changes: 40 additions & 0 deletions src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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.
*/
Expand Down
42 changes: 31 additions & 11 deletions src/dotty/tools/dotc/core/pickling/UnPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
43 changes: 38 additions & 5 deletions src/dotty/tools/dotc/transform/ElimRepeated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,16 +26,45 @@ 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 <repeated> 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)

private def elimRepeated(tp: Type)(implicit ctx: Context): Type = tp.stripTypeVar match {
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))
Expand Down Expand Up @@ -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
Expand Down
38 changes: 29 additions & 9 deletions src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand All @@ -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 {
Expand Down
21 changes: 20 additions & 1 deletion src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading