diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 264373baff93..247fa43365e1 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -51,7 +51,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { private def printTree(ctx: Context) = { val unit = ctx.compilationUnit - println(s"result of $unit after ${ctx.phase}:") + println(s"result of $unit after ${ctx.phase.prev}:") println(unit.tpdTree.show(ctx)) } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3664263e6d90..9a9ffe243dec 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -29,7 +29,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { untpd.Select(qualifier, tp.name).withType(tp) def Select(qualifier: Tree, sym: Symbol)(implicit ctx: Context): Select = - untpd.Select(qualifier, sym.name).withType(qualifier.tpe select sym) + untpd.Select(qualifier, sym.name).withType( + TermRef.withSig(qualifier.tpe, sym.name.asTermName, sym.signature, sym.denot)) def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit ctx: Context) = untpd.SelectWithSig(qualifier, name, sig) @@ -259,10 +260,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** new C(args) */ def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = { val targs = tp.argTypes + val constr = tp.typeSymbol.primaryConstructor.asTerm Apply( Select( New(tp withoutArgs targs), - TermRef(tp.normalizedPrefix, tp.typeSymbol.primaryConstructor.asTerm)) + TermRef.withSig(tp.normalizedPrefix, constr)) .appliedToTypes(targs), args) } diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index f69566733f1c..e77b10bfbaec 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -14,6 +14,13 @@ object Config { final val flagInstantiationToNothing = false + /** Enable noDoubleDef checking if option "-YnoDoubleDefs" is set. + * The reason to have an option as well as the present global switch is + * that the noDoubleDef checking is done in a hotspot, and we do not + * want to incur the overhead of checking an option each time. + */ + final val checkNoDoubleBindings = true + /** Throw an exception if a deep subtype recursion is detected */ final val flagDeepSubTypeRecursions = true diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 144e146c155c..aab2942bdfe4 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -163,6 +163,7 @@ class ScalaSettings extends Settings.SettingGroup { val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.") val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.") val Yexplainlowlevel = BooleanSetting("-Yexplainlowlevel", "When explaining type errors, show types at a lower level.") + val YnoDoubleBindings = BooleanSetting("-YnoDoubleBindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 4f95ff247209..b662326c5ef2 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -189,7 +189,6 @@ object Contexts { private var phasedCtx: Context = _ private var phasedCtxs: Array[Context] = _ - /** This context at given phase. * This method will always return a phase period equal to phaseId, thus will never return squashed phases */ @@ -211,7 +210,7 @@ object Contexts { final def withPhase(phase: Phase): Context = withPhase(phase.id) - /** If -Ydebug is on, the top of the stack trace where this context + /** If -Ydebug is on, the top of the stack trace where this context * was created, otherwise `null`. */ private var creationTrace: Array[StackTraceElement] = _ diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index e9b9b20c4f28..63b94efbd89f 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -273,35 +273,34 @@ object Denotations { def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation = if (denot1.signature matches denot2.signature) { + val sym1 = denot1.symbol + val sym2 = denot2.symbol val info1 = denot1.info val info2 = denot2.info - val sym2 = denot2.symbol - def sym2Accessible = sym2.isAccessibleFrom(pre) - if (info1 <:< info2 && sym2Accessible) denot2 + val sameSym = sym1 eq sym2 + if (sameSym && info1 <:< info2) denot2 + else if (sameSym && info2 <:< info1) denot1 else { - val sym1 = denot1.symbol - def sym1Accessible = sym1.isAccessibleFrom(pre) - if (info2 <:< info1 && sym1Accessible) denot1 - else { - val owner2 = if (sym2 ne NoSymbol) sym2.owner else NoSymbol - /** Determine a symbol which is overridden by both sym1 and sym2. - * Preference is given to accessible symbols. - */ - def lubSym(overrides: Iterator[Symbol], previous: Symbol): Symbol = - if (!overrides.hasNext) previous - else { - val candidate = overrides.next - if (owner2 derivesFrom candidate.owner) - if (candidate isAccessibleFrom pre) candidate - else lubSym(overrides, previous orElse candidate) - else - lubSym(overrides, previous) - } - new JointRefDenotation( - lubSym(sym1.allOverriddenSymbols, NoSymbol), - info1 | info2, - denot1.validFor & denot2.validFor) - } + val jointSym = + if (sameSym) sym1 + else { + val owner2 = if (sym2 ne NoSymbol) sym2.owner else NoSymbol + /** Determine a symbol which is overridden by both sym1 and sym2. + * Preference is given to accessible symbols. + */ + def lubSym(overrides: Iterator[Symbol], previous: Symbol): Symbol = + if (!overrides.hasNext) previous + else { + val candidate = overrides.next + if (owner2 derivesFrom candidate.owner) + if (candidate isAccessibleFrom pre) candidate + else lubSym(overrides, previous orElse candidate) + else + lubSym(overrides, previous) + } + lubSym(sym1.allOverriddenSymbols, NoSymbol) + } + new JointRefDenotation(jointSym, info1 | info2, denot1.validFor & denot2.validFor) } } else NoDenotation @@ -335,7 +334,7 @@ object Denotations { final def info(implicit ctx: Context) = infoOrCompleter final def validFor = denot1.validFor & denot2.validFor final def isType = false - def signature(implicit ctx: Context) = multiHasNot("signature") + final def signature(implicit ctx: Context) = Signature.OverloadedSignature def atSignature(sig: Signature)(implicit ctx: Context): SingleDenotation = denot1.atSignature(sig) orElse denot2.atSignature(sig) def current(implicit ctx: Context): Denotation = diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 35f94aac7ddf..22a7e5734048 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -50,6 +50,9 @@ object NameOps { implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ + def likeTyped(n: Name): N = + (if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N] + def isConstructorName = name == CONSTRUCTOR || name == IMPLCLASS_CONSTRUCTOR def isExceptionResultName = name startsWith EXCEPTION_RESULT_PREFIX def isImplClassName = name endsWith IMPL_CLASS_SUFFIX @@ -62,6 +65,8 @@ object NameOps { def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_SEPARATOR) def isSingletonName = name endsWith SINGLETON_SUFFIX def isModuleClassName = name endsWith MODULE_SUFFIX + def isImportName = name startsWith IMPORT + def isInheritedName = name.head == '(' && name.startsWith(nme.INHERITED) def isModuleVarName(name: Name): Boolean = name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX @@ -132,6 +137,10 @@ object NameOps { if (idx < 0) name else (name drop (idx + separator.length)).asInstanceOf[N] } + def inheritedName: N = likeTyped(nme.INHERITED ++ name) + + def revertInherited: N = likeTyped(name.drop(nme.INHERITED.length)) + /** Translate a name into a list of simple TypeNames and TermNames. * In all segments before the last, type/term is determined by whether * the following separator char is '.' or '#'. The last segment diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala index 02b91b18e42c..eb85fbb99e08 100644 --- a/src/dotty/tools/dotc/core/Signature.scala +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc package core -import Names._, Types._, Contexts._ +import Names._, Types._, Contexts._, StdNames._ import transform.Erasure.sigName /** The signature of a denotation. @@ -49,6 +49,10 @@ object Signature { * a type different from PolyType, MethodType, or ExprType. */ val NotAMethod = Signature(List(), EmptyTypeName) + + /** The signature of an overloaded denotation. + */ + val OverloadedSignature = Signature(List(tpnme.OVERLOADED), EmptyTypeName) /** The signature of a method with no parameters and result type `resultType`. */ def apply(resultType: Type, isJava: Boolean)(implicit ctx: Context): Signature = diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 3982c51f00ee..cb4272f7a267 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -100,6 +100,7 @@ object StdNames { val EXPAND_SEPARATOR: N = "$$" val IMPL_CLASS_SUFFIX: N = "$class" val IMPORT: N = "" + val INHERITED: N = "(inherited)" // tag to be used until we have proper name kinds val INTERPRETER_IMPORT_WRAPPER: N = "$iw" val INTERPRETER_LINE_PREFIX: N = "line" val INTERPRETER_VAR_PREFIX: N = "res" @@ -109,6 +110,7 @@ object StdNames { val MODULE_VAR_SUFFIX: N = "$module" val NAME_JOIN: N = NameTransformer.NAME_JOIN_STRING val USCORE_PARAM_PREFIX: N = "_$" + val OVERLOADED: N = "" val PACKAGE: N = "package" val PACKAGE_CLS: N = "package$" val PROTECTED_PREFIX: N = "protected$" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 7762f61dd7c5..dc76999f61b2 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -684,7 +684,8 @@ object SymDenotations { /** The symbol, in class `inClass`, that is overridden by this denotation. */ final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = - matchingSymbol(inClass, owner.thisType) + if ((this is Private) && (owner ne inClass)) NoSymbol + else matchingSymbol(inClass, owner.thisType) /** All symbols overriden by this denotation. */ final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = @@ -731,6 +732,9 @@ object SymDenotations { override def termRefWithSig(implicit ctx: Context): TermRef = TermRef.withSig(owner.thisType, name.asTermName, signature, this) + def nonMemberTermRef(implicit ctx: Context): TermRef = + TermRef.withNonMemberSym(owner.thisType, name.asTermName, symbol.asTerm) + /** The variance of this type parameter or type member as an Int, with * +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter */ diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index b4db26ae6cf8..8f66830e828c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -245,7 +245,7 @@ trait Symbols { this: Context => for (name <- names) { val tparam = newNakedSymbol[TypeName](NoCoord) tparamBuf += tparam - trefBuf += TypeRef(owner.thisType, name) withSym tparam + trefBuf += TypeRef(owner.thisType, name).withSym(tparam, Signature.NotAMethod) } val tparams = tparamBuf.toList val bounds = boundsFn(trefBuf.toList) @@ -319,7 +319,7 @@ object Symbols { type ThisName <: Name private[this] var _id: Int = nextId - //assert(_id != 5859) + //assert(_id != 12325) /** The unique id of this symbol */ def id = _id diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8799f9fd0474..c348e246c646 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -991,6 +991,8 @@ object Types { val prefix: Type val name: Name + type ThisType >: this.type <: NamedType + assert(prefix.isValueType || (prefix eq NoPrefix), s"invalid prefix $prefix") private[this] var lastDenotation: Denotation = _ @@ -1049,21 +1051,60 @@ object Types { if (owner.isTerm) d else d.asSeenFrom(prefix) } - private[dotc] final def withDenot(denot: Denotation): this.type = { + private def checkSymAssign(sym: Symbol) = + assert( + (lastSymbol eq sym) || + (lastSymbol eq null) || + (lastSymbol.defRunId != sym.defRunId) || + (lastSymbol.defRunId == NoRunId), + s"data race? overwriting symbol of $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id}") + + protected def sig: Signature = Signature.NotAMethod + + private[dotc] def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = + if (sig != denot.signature) + withSig(denot.signature).withDenot(denot).asInstanceOf[ThisType] + else { + setDenot(denot) + this + } + + private[dotc] final def setDenot(denot: Denotation)(implicit ctx: Context): Unit = { + if (Config.checkNoDoubleBindings) + if (ctx.settings.YnoDoubleBindings.value) + checkSymAssign(denot.symbol) lastDenotation = denot lastSymbol = denot.symbol - this } - private[dotc] final def withSym(sym: Symbol): this.type = { + private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType = + if (sig != signature) + withSig(signature).withSym(sym, signature).asInstanceOf[ThisType] + else { + setSym(sym) + this + } + + private[dotc] final def setSym(sym: Symbol)(implicit ctx: Context): Unit = { + if (Config.checkNoDoubleBindings) + if (ctx.settings.YnoDoubleBindings.value) + checkSymAssign(sym) + uncheckedSetSym(sym) + } + + private[dotc] final def uncheckedSetSym(sym: Symbol): Unit = { lastDenotation = null lastSymbol = sym checkedPeriod = Nowhere - this } + private def withSig(sig: Signature)(implicit ctx: Context): NamedType = + TermRef.withSig(prefix, name.asTermName, sig) + protected def loadDenot(implicit ctx: Context) = { - val d = prefix.member(name) + val d = + if (name.isInheritedName) prefix.nonPrivateMember(name.revertInherited) + else prefix.member(name) if (d.exists || ctx.phaseId == FirstPhaseId) d else {// name has changed; try load in earlier phase and make current @@ -1124,6 +1165,11 @@ object Types { protected def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType = NamedType(prefix, name) + /** Create a NamedType of the same kind as this type, but with a new name. + */ + final def shadowed(implicit ctx: Context): NamedType = + NamedType(prefix, name.inheritedName) + override def equals(that: Any) = that match { case that: NamedType => this.name == that.name && @@ -1136,6 +1182,9 @@ object Types { abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType { + type ThisType = TermRef + + //assert(name.toString != "") override def underlying(implicit ctx: Context): Type = { val d = denot if (d.isOverloaded) NoType else d.info @@ -1156,16 +1205,31 @@ object Types { } abstract case class TypeRef(override val prefix: Type, name: TypeName) extends NamedType { + + type ThisType = TypeRef + override def underlying(implicit ctx: Context): Type = info } - final class TermRefWithSignature(prefix: Type, name: TermName, val sig: Signature) extends TermRef(prefix, name) { + final class TermRefWithSignature(prefix: Type, name: TermName, override val sig: Signature) extends TermRef(prefix, name) { assert(prefix ne NoPrefix) override def signature(implicit ctx: Context) = sig - override def loadDenot(implicit ctx: Context): Denotation = - super.loadDenot.atSignature(sig) - override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = - TermRef.withSig(prefix, name, sig) + override def loadDenot(implicit ctx: Context): Denotation = { + val d = super.loadDenot + if (sig eq Signature.OverloadedSignature) d + else d.atSignature(sig) + } + + override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = { + if (sig != Signature.NotAMethod && + sig != Signature.OverloadedSignature && + symbol.exists) { + val ownSym = symbol + TermRef(prefix, name).withDenot(prefix.member(name).disambiguate(_ eq ownSym)) + } + else TermRef.withSig(prefix, name, sig) + } + override def equals(that: Any) = that match { case that: TermRefWithSignature => this.prefix == that.prefix && @@ -1177,15 +1241,25 @@ object Types { override def computeHash = doHash((name, sig), prefix) } - trait WithNoPrefix extends NamedType { + trait WithNonMemberSym extends NamedType { def fixedSym: Symbol assert(fixedSym ne NoSymbol) - withSym(fixedSym) + uncheckedSetSym(fixedSym) + + override def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = { + assert(denot.symbol eq fixedSym) + setDenot(denot) + this + } + + override def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType = + unsupported("withSym") + override def equals(that: Any) = that match { - case that: WithNoPrefix => this.fixedSym eq that.fixedSym + case that: WithNonMemberSym => this.prefix == that.prefix && (this.fixedSym eq that.fixedSym) case _ => false } - override def computeHash = doHash(fixedSym) + override def computeHash = doHash(fixedSym, prefix) } final class CachedTermRef(prefix: Type, name: TermName, hc: Int) extends TermRef(prefix, name) { @@ -1200,8 +1274,8 @@ object Types { override def computeHash = unsupported("computeHash") } - final class NoPrefixTermRef(name: TermName, val fixedSym: TermSymbol) extends TermRef(NoPrefix, name) with WithNoPrefix - final class NoPrefixTypeRef(name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(NoPrefix, name) with WithNoPrefix + final class NonMemberTermRef(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithNonMemberSym + final class NonMemberTypeRef(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithNonMemberSym object NamedType { def apply(prefix: Type, name: Name)(implicit ctx: Context) = @@ -1210,20 +1284,42 @@ object Types { def apply(prefix: Type, name: Name, denot: Denotation)(implicit ctx: Context) = if (name.isTermName) TermRef(prefix, name.asTermName, denot) else TypeRef(prefix, name.asTypeName, denot) + def withNonMemberSym(prefix: Type, sym: Symbol)(implicit ctx: Context) = + if (sym.isType) TypeRef.withNonMemberSym(prefix, sym.name.asTypeName, sym.asType) + else TermRef.withNonMemberSym(prefix, sym.name.asTermName, sym.asTerm) } object TermRef { def apply(prefix: Type, name: TermName)(implicit ctx: Context): TermRef = ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TermRef] + def apply(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = withSymAndName(prefix, sym, sym.name) + + def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = { + if (prefix eq NoPrefix) apply(prefix, denot.symbol.asTerm) + else denot match { + case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature) + case _ => apply(prefix, name) + } + } withDenot denot + + def withNonMemberSym(prefix: Type, name: TermName, sym: TermSymbol)(implicit ctx: Context): TermRef = + unique(new NonMemberTermRef(prefix, name, sym)) + def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = - if (prefix eq NoPrefix) unique(new NoPrefixTermRef(name, sym)) - else apply(prefix, name) withSym sym - def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = - (if (prefix eq NoPrefix) apply(prefix, denot.symbol.asTerm) else apply(prefix, name)) withDenot denot + if (prefix eq NoPrefix) withNonMemberSym(prefix, name, sym) + else { + if (sym.defRunId != NoRunId && sym.isCompleted) withSig(prefix, name, sym.signature) + else apply(prefix, name) + } withSym (sym, Signature.NotAMethod) + + def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = + unique(withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature)) + def withSig(prefix: Type, name: TermName, sig: Signature)(implicit ctx: Context): TermRef = unique(new TermRefWithSignature(prefix, name, sig)) + def withSig(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = (if (prefix eq NoPrefix) apply(prefix, denot.symbol.asTerm) else withSig(prefix, name, sig)) withDenot denot @@ -1232,11 +1328,17 @@ object Types { object TypeRef { def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef] + def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef = withSymAndName(prefix, sym, sym.name) + + def withNonMemberSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = + unique(new NonMemberTypeRef(prefix, name, sym)) + def withSymAndName(prefix: Type, sym: TypeSymbol, name: TypeName)(implicit ctx: Context): TypeRef = - if (prefix eq NoPrefix) unique(new NoPrefixTypeRef(name, sym)) - else apply(prefix, name) withSym sym + if (prefix eq NoPrefix) withNonMemberSym(prefix, name, sym) + else apply(prefix, name).withSym(sym, Signature.NotAMethod) + def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef = (if (prefix eq NoPrefix) apply(prefix, denot.symbol.asType) else apply(prefix, name)) withDenot denot } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 557a2b5db6f8..f9cd9ec727ce 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -3,7 +3,7 @@ package printing import core._ import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._ -import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation +import Contexts.Context, Scopes.Scope, Denotations._, Annotations.Annotation import StdNames.nme import ast.{Trees, untpd} import typer.Namer @@ -475,7 +475,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { Text(flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ") } - override def toText(denot: Denotation): Text = toText(denot.symbol) + override def toText(denot: Denotation): Text = denot match { + case denot: MultiDenotation => denot.toString + case _ => + if (denot.symbol.exists) toText(denot.symbol) + else "some " ~ toText(denot.info) + } override def plain = new PlainPrinter(_ctx) } diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala index 43a91a058c51..978a9cce44d1 100644 --- a/src/dotty/tools/dotc/transform/Splitter.scala +++ b/src/dotty/tools/dotc/transform/Splitter.scala @@ -3,10 +3,10 @@ package transform import TreeTransforms._ import ast.Trees._ -import core.Contexts._ -import core.Types._ +import core._ +import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotations._, Names._ -/** This transform makes usre every identifier and select node +/** This transform makes sure every identifier and select node * carries a symbol. To do this, certain qualifiers with a union type * have to be "splitted" with a type test. * @@ -24,4 +24,104 @@ class Splitter extends TreeTransform { This(cls) withPos tree.pos case _ => tree } + + /** If we select a name, make sure the node has a symbol. + * If necessary, split the qualifier with type tests. + * Example: Assume: + * + * class A { def f(x: S): T } + * class B { def f(x: S): T } + * def p(): A | B + * + * Then p().f(a) translates to + * + * val ev$1 = p() + * if (ev$1.isInstanceOf[A]) ev$1.asInstanceOf[A].f(a) + * else ev$1.asInstanceOf[B].f(a) + */ + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { + val Select(qual, name) = tree + + def memberDenot(tp: Type): SingleDenotation = { + val mbr = tp.member(name) + if (!mbr.isOverloaded) mbr.asSingleDenotation + else tree.tpe match { + case tref: TermRefWithSignature => mbr.atSignature(tref.sig) + case _ => ctx.error(s"cannot disambiguate overloaded member $mbr"); NoDenotation + } + } + + def candidates(tp: Type): List[Symbol] = { + val mbr = memberDenot(tp) + if (mbr.symbol.exists) mbr.symbol :: Nil + else tp.widen match { + case tref: TypeRef => + tref.info match { + case TypeBounds(_, hi) => candidates(hi) + case _ => Nil + } + case OrType(tp1, tp2) => + candidates(tp1) | candidates(tp2) + case AndType(tp1, tp2) => + candidates(tp1) & candidates(tp2) + case tpw => + Nil + } + } + + def isStructuralSelect(tp: Type): Boolean = tp.stripTypeVar match { + case tp: RefinedType => tp.refinedName == name || isStructuralSelect(tp) + case tp: TypeProxy => isStructuralSelect(tp.underlying) + case AndType(tp1, tp2) => isStructuralSelect(tp1) || isStructuralSelect(tp2) + case _ => false + } + + if (tree.symbol.exists) tree + else { + def choose(qual: Tree, syms: List[Symbol]): Tree = { + def testOrCast(which: Symbol, mbr: Symbol) = + TypeApply(Select(qual, which), TypeTree(mbr.owner.typeRef) :: Nil) + def select(sym: Symbol) = { + val qual1 = + if (qual.tpe derivesFrom sym.owner) qual + else testOrCast(defn.Any_asInstanceOf, sym) + Select(qual1, sym) withPos tree.pos + } + syms match { + case Nil => + def msg = + if (isStructuralSelect(qual.tpe)) + s"cannot access member '$name' from structural type ${qual.tpe.widen.show}; use Dynamic instead" + else + s"no candidate symbols for ${tree.tpe.show} found in ${qual.tpe.show}" + ctx.error(msg, tree.pos) + tree + case sym :: Nil => + select(sym) + case sym :: syms1 => + If(testOrCast(defn.Any_isInstanceOf, sym), select(sym), choose(qual, syms1)) + } + } + evalOnce(qual)(qual => choose(qual, candidates(qual.tpe))) + } + } + + /** Distribute arguments among splitted branches */ + def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = { + def recur(fn: Tree): Tree = fn match { + case Block(stats, expr) => Block(stats, recur(expr)) + case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep)) + case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos + } + recur(tree.fun) + } + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) = + distribute(tree, typeApply) + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + distribute(tree, apply) + + private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx) + private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx) } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 5913875b832e..f52c5bc1c94d 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -12,8 +12,10 @@ import core.Constants._ import core.StdNames._ import core.Decorators._ import core.transform.Erasure.isUnboundedGeneric +import typer._ import typer.ErrorReporting._ import ast.Trees._ +import ast.{tpd, untpd} /** This transform eliminates patterns. Right now it's a dummy. * Awaiting the real pattern matcher. @@ -23,14 +25,37 @@ class TreeChecker { def check(ctx: Context) = { println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}") - Checker.transform(ctx.compilationUnit.tpdTree)(ctx) + Checker.typedExpr(ctx.compilationUnit.tpdTree)(ctx) } - object Checker extends TreeMap { - override def transform(tree: Tree)(implicit ctx: Context) = { - println(i"checking $tree") - assert(tree.isEmpty || tree.hasType, tree.show) - super.transform(tree) + object Checker extends ReTyper { + override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = + if (tree.isEmpty) tree.asInstanceOf[Tree] + else { + assert(tree.hasType, tree.show) + val tree1 = super.typed(tree, pt) + def sameType(tp1: Type, tp2: Type) = + (tp1 eq tp2) || // accept NoType / NoType + (tp1 =:= tp2) + def divergenceMsg = + s"""Types differ + |Original type : ${tree.typeOpt.show} + |After checking: ${tree1.tpe.show} + |Original tree : ${tree.show} + |After checking: ${tree1.show} + """.stripMargin + assert(sameType(tree1.tpe, tree.typeOpt), divergenceMsg) + tree1 + } + + override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { + assert(tree.isTerm, tree.show) + super.typedIdent(tree, pt) + } + + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { + assert(tree.isTerm, tree.show) + super.typedSelect(tree, pt) } } } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 57f11480cae4..0f47336fcedb 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -794,8 +794,8 @@ trait Applications extends Compatibility { self: Typer => tp } - val owner1 = alt1.symbol.owner - val owner2 = alt2.symbol.owner + val owner1 = if (alt1.symbol.exists) alt1.symbol.owner else NoSymbol + val owner2 = if (alt2.symbol.exists) alt2.symbol.owner else NoSymbol val tp1 = stripImplicit(alt1.widen) val tp2 = stripImplicit(alt2.widen) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 287e93c7af23..ae325af2a449 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -102,13 +102,14 @@ trait Checking { tp } - /** Check that class does not define */ + /** Check that class does not define same symbol twice */ def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { val seen = new mutable.HashMap[Name, List[Symbol]] { override def default(key: Name) = Nil } typr.println(i"check no double defs $cls") - for (decl <- cls.info.decls) { + + def checkDecl(decl: Symbol): Unit = { for (other <- seen(decl.name)) { typr.println(i"conflict? $decl $other") if (decl.signature matches other.signature) { @@ -129,6 +130,12 @@ trait Checking { } seen(decl.name) = decl :: seen(decl.name) } + + cls.info.decls.foreach(checkDecl) + cls.info match { + case ClassInfo(_, _, _, _, selfSym: Symbol) => checkDecl(selfSym) + case _ => + } } def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 2425bea5024a..2d4bf809951c 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -209,7 +209,7 @@ class Namer { typer: Typer => ctx.error(s"${preExisting.showLocated} is compiled twice, runid = ${ctx.runId}", tree.pos) } else if ((!ctx.owner.isClass || name.isTypeName) && preExisting.exists) { - ctx.error(d"$name is already defined as $preExisting") + ctx.error(i"$name is already defined as $preExisting", tree.pos) } } @@ -474,7 +474,6 @@ class Namer { typer: Typer => index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) - // make sure constr parameters are all entered because we refer to them in desugarings: } } diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 896dbba7dc2f..c2f627b1e7b9 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -3,7 +3,7 @@ package typer import core.Contexts._ import core.Types._ -import core.Symbols.Symbol +import core.Symbols._ import typer.ProtoTypes._ import ast.{tpd, untpd} import ast.Trees._ @@ -48,6 +48,8 @@ class ReTyper extends Typer { untpd.cpy.Bind(tree, tree.name, body1).withType(tree.typeOpt) } + override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol + override def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol override def localTyper(sym: Symbol) = this diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 5821b58b2699..12f2415d89da 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -109,7 +109,8 @@ trait TypeAssigner { // it could be that we found an inaccessbile private member, but there is // an inherited non-private member with the same name and signature. val d2 = pre.nonPrivateMember(name) - if (reallyExists(d2) && firstTry) test(pre.select(name, d2), false) + if (reallyExists(d2) && firstTry) + test(tpe.shadowed.withDenot(d2), false) else { val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) val what = alts match { @@ -325,7 +326,7 @@ trait TypeAssigner { tree.withType(sym.typeRef) def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) = - tree.withType(sym.termRef) + tree.withType(sym.nonMemberTermRef) def assignType(tree: untpd.Annotated, annot: Tree, arg: Tree)(implicit ctx: Context) = tree.withType(AnnotatedType(Annotation(annot), arg.tpe)) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 60acda4b7a84..6e613975b295 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -809,11 +809,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val parents1 = ensureConstrCall(ensureFirstIsClass( parents mapconserve typedParent, cdef.pos.toSynthetic)) val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class memebers are not visible - val localDummy = ctx.newLocalDummy(cls, impl.pos) - val body1 = typedStats(body, localDummy)(inClassContext(self1.symbol)) + val dummy = localDummy(cls, impl) + val body1 = typedStats(body, dummy)(inClassContext(self1.symbol)) checkNoDoubleDefs(cls) val impl1 = cpy.Template(impl, constr1, parents1, self1, body1) - .withType(localDummy.termRef) + .withType(dummy.termRef) assignType(cpy.TypeDef(cdef, mods1, name, impl1), cls) // todo later: check that @@ -825,6 +825,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // 4. Polymorphic type defs override nothing. } + def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context): Symbol = + ctx.newLocalDummy(cls, impl.pos) + def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.pos) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index dcd5c67c8649..3228c3474c28 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -14,7 +14,7 @@ class tests extends CompilerTest { // "-Yshow-suppressed-errors", "-pagewidth", "160" ) - val twice = List("#runs", "2") + val twice = List("#runs", "2", "-YnoDoubleBindings") val doErase = List("-Ystop-before:terminal") val posDir = "./tests/pos/" @@ -55,7 +55,7 @@ class tests extends CompilerTest { @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) @Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2) @Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3) - @Test def neg_typers() = compileFile(negDir, "typers", xerrors = 10) + @Test def neg_typers() = compileFile(negDir, "typers", xerrors = 13) @Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2) @Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2) @Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3) @@ -65,6 +65,8 @@ class tests extends CompilerTest { @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 1) @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 4) @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) + @Test def neg_t0586_structural = compileFile(negDir, "t0586", xerrors = 1) + @Test def neg_t0625_structural = compileFile(negDir, "t0625", xerrors = 1) @Test def neg_t0654_polyalias = compileFile(negDir, "t0654", xerrors = 2) @Test def neg_t1192_legalPrefix = compileFile(negDir, "t1192", xerrors = 1) diff --git a/tests/pos/t0586.scala b/tests/neg/t0586.scala similarity index 100% rename from tests/pos/t0586.scala rename to tests/neg/t0586.scala diff --git a/tests/pos/t0625.scala b/tests/neg/t0625.scala similarity index 100% rename from tests/pos/t0625.scala rename to tests/neg/t0625.scala diff --git a/tests/neg/typers.scala b/tests/neg/typers.scala index d25a8911e893..2f1cf40c4500 100644 --- a/tests/neg/typers.scala +++ b/tests/neg/typers.scala @@ -1,40 +1,56 @@ object typers { - + + class A(x: Int) { + val x: String = "a" // error: double def + + { val y: String = "" + val y: Int = 0 // error: double def + y + } + } + + class B { self => // error: double def + def self: Int = 0 + def self(x: Int): Int = x + } + class C { val x: Int - val x: String + val x: String // error: double def val y: Int - def y: String + def y: String // error: double def val z: Int - def z(): String - - def f(x: Any) = () + def z(): String // error: double def + + def f(x: Any) = () // error: double def def f(x: AnyRef): AnyRef - + def g(x: Object): Unit - def g[T](x: T): T = x + def g[T](x: T): T = x // error: double def } - - + + + + object returns { - - def foo(x: Int) = { + + def foo(x: Int) = { // error: has return; needs result type return 3 } - - return 4 + + return 4 // error: return outside method definition } - + object cyclic { - def factorial(acc: Int, n: Int) = + def factorial(acc: Int, n: Int) = if (n == 0) acc - else factorial(acc * n, n - 1) - - def foo(x: Int) = x + else factorial(acc * n, n - 1) // error: cyclic reference + + def foo(x: Int) = x // error: cyclic reference def foo() = foo(1) - + } - + object tries { val x = try { @@ -46,6 +62,6 @@ object typers { } class Refinements { - val y: C { val x: T; type T } + val y: C { val x: T; type T } // error: illegal forward reference in refinement } -} \ No newline at end of file +} diff --git a/tests/pos/alias.scala b/tests/pos/alias.scala new file mode 100644 index 000000000000..a66edc73a121 --- /dev/null +++ b/tests/pos/alias.scala @@ -0,0 +1,3 @@ +class A(val x: Int) + +class B(x: Int) extends A(x) diff --git a/tests/pos/typers.scala b/tests/pos/typers.scala index cb3bbc590e44..4f012e7bf306 100644 --- a/tests/pos/typers.scala +++ b/tests/pos/typers.scala @@ -1,61 +1,61 @@ -package test +package test import annotation.{tailrec, switch} import collection.mutable._ object typers { - + val names = List("a", "b", "c") val ints = List(1, 2, 3) - + object Inference { for ((name, n) <- (names, ints).zipped) println(name.length + n) - + def double(x: Char): String = s"$x$x" - + "abc" flatMap double - + } object Eta { - + def fun(x: Int): Int = x + 1 val foo = fun(_) } - + case class DefaultParams(init: String => String = identity) object DefaultParams { def foo(x: String => String = identity) = x("abc") - + foo() } - + class List[+T] { def :: (x: T) = new :: (x, this) - + def len: Int = this match { case x :: xs1 => 1 + xs1.len case Nil => 0 } } - + object Nil extends List[Nothing] - + case class :: [+T] (hd: T, tl: List[T]) extends List[T] - + def len[U](xs: List[U]): Int = xs match { case x :: xs1 => 1 + len(xs1) case Nil => 0 } - + object returns { - + def foo(x: Int): Int = { return 3 } } - + object tries { val x = try { @@ -70,69 +70,71 @@ object typers { val y = try 2 catch Predef.identity val z = try 3 finally "abc" - + println("abc".toString) } class C { - + @tailrec def factorial(acc: Int, n: Int): Int = (n: @switch) match { case 0 => acc case _ => factorial(acc * n, n - 1) } - + println(factorial(1, 10)) - - + + } - + class Refinements { val y: C { type T; val key: T; def process(x: T): Int } } - + object Accessibility { - + class A { val x: String = "abc" } - + class B extends A { private def x: Int = 1 } - + val b: B = new B val y = b.x val z: String = y - + } - + object Self { - - class A(self: Int) { self => - + + class A(self1: Int) { self => + + def self1(x: Int) = x + class B { val b = self val c: A = b } - + val a = self val c: A = a } - - + + } - + object Arrays { - + val arr = List("a", "b", "c").toArray val i = 2 arr(i).charAt(0) - + val x = new ArrayBuffer[String] // testing overloaded polymorphic constructors val entries = Array("abc", "def") - + for ((x, i) <- entries.zipWithIndex) println(x) } @@ -145,6 +147,6 @@ object typers { } val yy: String = y } - -} \ No newline at end of file + +} diff --git a/tests/pos/unions.scala b/tests/pos/unions.scala index 779d1847e97a..e57a96fb9010 100644 --- a/tests/pos/unions.scala +++ b/tests/pos/unions.scala @@ -2,13 +2,31 @@ object unions { class A { def f: String = "abc" + + def g(x: Int): Int = x + def g(x: Double): Double = x } class B { def f: String = "bcd" + + def g(x: Int) = -x + def g(x: Double): Double = -x } val x: A | B = if (true) new A else new B + def y: B | A = if (true) new A else new B println(x.f) + println(x.g(2)) + println(y.f) + println(y.g(1.0)) + + class C { + private def foo = 0 + class D extends C { + private def foo = 1 + def test(cd: C | D, dc: D | C) = (cd.foo, dc.foo) + } + } }