Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ object desugar {
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil),
AppliedTypeTree(ref(seqClass.typeRef), t))
} else {
assert(ctx.mode.isExpr, ctx.mode)
assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
Select(t, op)
}
case PrefixOp(op, t) =>
Expand Down
6 changes: 6 additions & 0 deletions src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case Block(stats, expr) => methPart(expr)
case mp => mp
}

/** If tree is an instance creation expression, its New node, otherwise tree itself */
def newPart(tree: Tree) = methPart(tree) match {
case Select(nu: untpd.New, nme.CONSTRUCTOR) => nu
case _ => tree
}

/** If tree is a closure, it's body, otherwise tree itself */
def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
Expand Down
7 changes: 7 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ object SymDenotations {
/** is this symbol a trait representing a type lambda? */
final def isLambdaTrait(implicit ctx: Context): Boolean =
isClass && name.startsWith(tpnme.LambdaPrefix)

/** Is this symbol an annotation? */
final def isAnnotation(implicit ctx: Context): Boolean =
derivesFrom(defn.AnnotationClass)

/** Is this symbol a package object or its module class? */
def isPackageObject(implicit ctx: Context): Boolean = {
Expand Down Expand Up @@ -986,6 +990,9 @@ object SymDenotations {

def nonMemberTermRef(implicit ctx: Context): TermRef =
TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm)

def typeRefWithArgs(implicit ctx: Context): Type =
typeRef.appliedTo(typeParams.map(_.typeRef))

/** The variance of this type parameter or type member as an Int, with
* +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter
Expand Down
6 changes: 2 additions & 4 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,7 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ => default
}
case tp @ RefinedType(parent, name) if !tp.member(name).symbol.is(ExpandedTypeParam) =>
val pbase = parent.baseTypeWithArgs(base)
if (pbase.member(name).exists) RefinedType(pbase, name, tp.refinedInfo)
else pbase
tp.wrapIfMember(parent.baseTypeWithArgs(base))
case tp: TermRef =>
tp.underlying.baseTypeWithArgs(base)
case AndType(tp1, tp2) =>
Expand All @@ -281,7 +279,7 @@ class TypeApplications(val self: Type) extends AnyVal {
default
}
}

/** Translate a type of the form From[T] to To[T], keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
*/
Expand Down
45 changes: 31 additions & 14 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,13 @@ object Types {
case _ => defn.AnyClass.typeRef
}

/** the self type of the underlying classtype */
def givenSelfType(implicit ctx: Context): Type = this match {
case tp @ RefinedType(parent, name) => tp.wrapIfMember(parent.givenSelfType)
case tp: TypeProxy => tp.underlying.givenSelfType
case _ => NoType
}

/** The parameter types of a PolyType or MethodType, Empty list for others */
final def paramTypess(implicit ctx: Context): List[List[Type]] = this match {
case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess
Expand Down Expand Up @@ -1781,7 +1788,12 @@ object Types {
if (false) RefinedType(parent, refinedName, refinedInfo)
else RefinedType(parent, refinedName, rt => refinedInfo.substSkolem(this, SkolemType(rt)))
}


/** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */
def wrapIfMember(parent: Type)(implicit ctx: Context): Type =
if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo)
else parent

override def equals(that: Any) = that match {
case that: RefinedType =>
this.parent == that.parent &&
Expand Down Expand Up @@ -2398,22 +2410,27 @@ object Types {
* - the fully applied reference to the class itself.
*/
def selfType(implicit ctx: Context): Type = {
if (selfTypeCache == null) {
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
def withFullRef(tp: Type): Type =
if (ctx.erasedTypes) fullRef else AndType(tp, fullRef)
selfTypeCache = selfInfo match {
case NoType =>
fullRef
case tp: Type =>
if (cls is Module) tp else withFullRef(tp)
case self: Symbol =>
assert(!(cls is Module))
withFullRef(self.info)
if (selfTypeCache == null)
selfTypeCache = {
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
val given = givenSelfType
val raw =
if (!given.exists) fullRef
else if (cls is Module) given
else if (ctx.erasedTypes) fullRef
else AndType(given, fullRef)
raw//.asSeenFrom(prefix, cls.owner)
}
}
selfTypeCache
}

/** The explicitly given self type (self types of modules are assumed to be
* explcitly given here).
*/
override def givenSelfType(implicit ctx: Context): Type = selfInfo match {
case tp: Type => tp
case self: Symbol => self.info
}

private var selfTypeCache: Type = null

Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ class PlainPrinter(_ctx: Context) extends Printer {

def toText(sym: Symbol): Text =
(kindString(sym) ~~ {
if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
if (sym.isAnonymousClass) toText(sym.info.parents, " with ") ~ "{...}"
else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
else nameString(sym)
}).close

Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/reporting/ConsoleReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ConsoleReporter(
}

override def doReport(d: Diagnostic)(implicit ctx: Context): Unit =
if (!d.isSuppressed) d match {
if (!d.isSuppressed || !hasErrors) d match {
case d: Error =>
printMessageAndPos(s"error: ${d.msg}", d.pos)
if (ctx.settings.prompt.value) displayPrompt()
Expand Down
28 changes: 28 additions & 0 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ trait Applications extends Compatibility { self: Typer =>

methPart(fun1).tpe match {
case funRef: TermRef =>
labelAnnotArgs(funRef.symbol, tree)
tryEither { implicit ctx =>
val app =
if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
Expand Down Expand Up @@ -578,6 +579,33 @@ trait Applications extends Compatibility { self: Typer =>
else realApply
}

/** If `meth` is a constructor of a Java annotation used as an annotation
* (as opposed to a normal `new`), label every `new` everywhere in its arguments
* (including nested occurrences) as annotations.
* See pos/java-interop/t294 for examples.
* This labeling is safe because Java annotations only accept constants and
* other annotations as arguments, so a `new` must be an allocation site for
* an annotation.
* The labeling is necessary because we need to avoid flagging `new`'s of Java
* annotation interfaces as errors because the interface is abstract.
*/
private def labelAnnotArgs(meth: Symbol, app: untpd.Apply)(implicit ctx: Context): Unit = {
import untpd._
val isJavaAnnot = meth.isConstructor && meth.is(JavaDefined) && meth.owner.isAnnotation
if (newPart(app).hasAttachment(AnnotNew) && isJavaAnnot) {
val labelNewsAsAnnots = new TreeTraverser {
def traverse(tree: untpd.Tree)(implicit ctx: Context): Unit = {
tree match {
case tree: New => tree.putAttachment(AnnotNew, ())
case _ =>
}
traverseChildren(tree)
}
}
app.args.foreach(labelNewsAsAnnots.traverse)
}
}

/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
Expand Down
23 changes: 17 additions & 6 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ trait Checking {

/** Check that type `tp` is stable. */
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable)
if (!tp.isStable && !tp.isErroneous)
ctx.error(d"$tp is not stable", pos)

/** Check that type `tp` is a legal prefix for '#'.
Expand All @@ -241,16 +241,27 @@ trait Checking {
def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isLegalPrefixFor(selector)) ctx.error(d"$tp is not a valid prefix for '# $selector'", pos)

/** Check that `tp` is a class type with a stable prefix. Also, if `traitReq` is
* true check that `tp` is a trait.
/** Check that `tp` is a class type with a stable prefix. Also:
* If `traitReq` is true, check that `tp` refers to a trait.
* If `concreteReq` is true, check that `tp` refers to a nonAbstract class
* and that the instance conforms to the self type of the created class.
* Stability checking is disabled in phases after RefChecks.
* @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not.
*/
def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean = false, concreteReq: Boolean = false)(implicit ctx: Context): Type =
tp.underlyingClassRef(refinementOK = false) match {
case tref: TypeRef =>
val cls = tref.symbol
if (ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(d"$tref is not a trait", pos)
if (traitReq && !(cls is Trait))
ctx.error(d"$tref is not a trait", pos)
if (concreteReq) {
if (cls.is(AbstractOrTrait))
ctx.error(d"$cls is abstract; cannot be instantiated", pos)
val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner)
if (!cls.is(Module) && selfType.exists && !(tp <:< selfType))
ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated")
}
tp
case _ =>
ctx.error(d"$tp is not a class type", pos)
Expand Down Expand Up @@ -329,7 +340,7 @@ trait NoChecking extends Checking {
override def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = ()
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
override def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = ()
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean, concreteReq: Boolean)(implicit ctx: Context): Type = tp
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
Expand Down
14 changes: 13 additions & 1 deletion src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,21 @@ class Namer { typer: Typer =>

import untpd._

/** Untyped tree was already typed, the attachment value is its typed versuon */
val TypedAhead = new Attachment.Key[tpd.Tree]

/** Tree was expanded to by desugaring; the attachment value is its expansion */
val ExpandedTree = new Attachment.Key[Tree]

/** Tree has been assigned a symbol, which is the attachment value */
val SymOfTree = new Attachment.Key[Symbol]

/** `New` node is part of a parent constructor of a class, and can therefore be abstract */
val ParentNew = new Attachment.Key[Unit]

/** `New` node is an annotation, can be abstract if the annotation is Java defined */
val AnnotNew = new Attachment.Key[Unit]

/** A partial map from unexpanded member and pattern defs and to their expansions.
* Populated during enterSyms, emptied during typer.
*/
Expand Down Expand Up @@ -528,7 +539,8 @@ class Namer { typer: Typer =>
case TypeApply(core, targs) => (core, targs)
case core => (core, Nil)
}
val Select(New(tpt), nme.CONSTRUCTOR) = core
val Select(nu @ New(tpt), nme.CONSTRUCTOR) = core
nu.putAttachment(ParentNew, ())
val targs1 = targs map (typedAheadType(_))
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
if (ptype.typeParams.isEmpty) ptype
Expand Down
12 changes: 12 additions & 0 deletions src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ object RefChecks {
}
}

/** Check that self type of this class conforms to self types of parents */
private def checkSelfType(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match {
case cinfo: ClassInfo =>
for (parent <- cinfo.classParents) {
val pself = parent.givenSelfType.asSeenFrom(clazz.thisType, parent.classSymbol)
if (pself.exists && !(cinfo.selfType <:< pself))
ctx.error(d"illegal inheritance: self type ${cinfo.selfType} of $clazz does not conform to self type $pself of parent ${parent.classSymbol}", clazz.pos)
}
case _ =>
}

// Override checking ------------------------------------------------------------

/** 1. Check all members of class `clazz` for overriding conditions.
Expand Down Expand Up @@ -770,6 +781,7 @@ class RefChecks extends MiniPhase with SymTransformer { thisTransformer =>
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
val cls = ctx.owner
checkOverloadedRestrictions(cls)
checkSelfType(cls)
checkAllOverrides(cls)
checkAnyValSubclass(cls)
tree
Expand Down
31 changes: 23 additions & 8 deletions src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import EtaExpansion.etaExpand
import dotty.tools.dotc.transform.Erasure.Boxing
import util.Positions._
import util.common._
import util.SourcePosition
import util.{SourcePosition, Attachment}
import collection.mutable
import annotation.tailrec
import Implicits._
Expand Down Expand Up @@ -347,8 +347,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val clsDef = TypeDef(x, templ).withFlags(Final)
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
case _ =>
val tpt1 = typedType(tree.tpt)
checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false)
val tpt1 = typedType(tree.tpt)
val cls = tpt1.tpe.classSymbol
val isJavaAnnot =
tree.hasAttachment(AnnotNew) && cls.is(JavaDefined) && cls.isAnnotation
val canBeAbstract =
tree.hasAttachment(ParentNew) || isJavaAnnot || ctx.isAfterTyper
checkClassTypeWithStablePrefix(tpt1.tpe, tree.pos, concreteReq = !canBeAbstract)
assignType(cpy.New(tree)(tpt1), tpt1)
// todo in a later phase: checkInstantiatable(cls, tpt1.pos)
}
Expand Down Expand Up @@ -868,7 +873,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}

def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") {
typed(annot, defn.AnnotationClass.typeRef)
labelNew(annot, AnnotNew)
typedExpr(annot, defn.AnnotationClass.typeRef)
}

def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") {
Expand Down Expand Up @@ -940,11 +946,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
*/
def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = {
val firstParent :: otherParents = parents
if (firstParent.isType && !(cls is Trait))
typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents
else parents
if (firstParent.isType && !(cls is Trait)) {
val constr = untpd.New(untpd.TypedSplice(firstParent), Nil)
labelNew(constr, ParentNew)
typed(constr) :: otherParents
} else parents
}

/** If `tree` is an instance creation expression, attach the given label to its `New` node */
def labelNew(tree: untpd.Tree, label: Attachment.Key[Unit]): Unit =
untpd.newPart(tree) match {
case nu: untpd.New => nu.putAttachment(label, ())
case _ =>
}

/** Overridden in retyper */
def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree)

Expand All @@ -971,7 +986,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}

def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") {
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
val annot1 = typedAnnotation(tree.annot)
val arg1 = typed(tree.arg, pt)
if (ctx.mode is Mode.Type)
assignType(cpy.Annotated(tree)(annot1, arg1), annot1, arg1)
Expand Down
10 changes: 9 additions & 1 deletion src/dotty/tools/dotc/util/Attachment.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ object Attachment {
val nx = next
if (nx == null) None
else if (nx.key eq key) Some(nx.value.asInstanceOf[V])
else nx.getAttachment[V](key)
else nx.getAttachment(key)
}

/** Does tree have an attachment corresponding to `key`? */
final def hasAttachment[V](key: Key[V]): Boolean = {
val nx = next
if (nx == null) false
else if (nx.key eq key) true
else nx.hasAttachment(key)
}

/** The attachment corresponding to `key`.
Expand Down
2 changes: 2 additions & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class tests extends CompilerTest {
@Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3)
@Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4)
@Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2)
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)

@Test def dotc = compileDir(dotcDir + "tools/dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", failedOther ++ twice)
Expand Down
Loading