From 9c8e247d8900f3fb6f497bc5e269b68f6bc967b7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 17:29:53 +0200 Subject: Matching denotations should take infos into account Whenchecking whether two denotations match it is not enough to look at the signatures. The signatures might match (on the parameters) but the actual parametre types might be different. The change always tests infos after signatures, effectively turning the signature test into a pre-filter. --- src/dotty/tools/dotc/core/Denotations.scala | 77 ++++++++++++++------------- src/dotty/tools/dotc/typer/Checking.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 +- test/dotc/tests.scala | 3 +- tests/neg/typers.scala | 4 +- 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 16a151e89..cd46918cf 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -268,50 +268,52 @@ object Denotations { } case denot1: SingleDenotation => if (denot1 eq denot2) denot1 - else if (denot1.signature matches denot2.signature) { + else { val info1 = denot1.info val info2 = denot2.info - val sym1 = denot1.symbol - val sym2 = denot2.symbol - val sym2Accessible = sym2.isAccessibleFrom(pre) - - /** Does `sym1` come before `sym2` in the linearization of `pre`? */ - def precedes(sym1: Symbol, sym2: Symbol) = { - def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) - case Nil => true + if (denot1.signature.matches(denot2.signature) && + denot1.info.matches(denot2.info)) { + val sym1 = denot1.symbol + val sym2 = denot2.symbol + val sym2Accessible = sym2.isAccessibleFrom(pre) + + /** Does `sym1` come before `sym2` in the linearization of `pre`? */ + def precedes(sym1: Symbol, sym2: Symbol) = { + def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) + case Nil => true + } + sym1.derivesFrom(sym2) || + !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) } - sym1.derivesFrom(sym2) || - !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) - } - /** Preference according to partial pre-order (isConcrete, precedes) */ - def preferSym(sym1: Symbol, sym2: Symbol) = - sym1.eq(sym2) || - sym1.isAsConcrete(sym2) && - (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) + /** Preference according to partial pre-order (isConcrete, precedes) */ + def preferSym(sym1: Symbol, sym2: Symbol) = + sym1.eq(sym2) || + sym1.isAsConcrete(sym2) && + (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) - /** Sym preference provided types also override */ - def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = - preferSym(sym1, sym2) && info1.overrides(info2) + /** Sym preference provided types also override */ + def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = + preferSym(sym1, sym2) && info1.overrides(info2) - if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 - else { - val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 - else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 - else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 + if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 else { - val sym = - if (!sym1.exists) sym2 - else if (!sym2.exists) sym1 - else if (preferSym(sym2, sym1)) sym2 - else sym1 - new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) + val sym1Accessible = sym1.isAccessibleFrom(pre) + if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 + else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 + else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 + else { + val sym = + if (!sym1.exists) sym2 + else if (!sym2.exists) sym1 + else if (preferSym(sym2, sym1)) sym2 + else sym1 + new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) + } } - } + } else NoDenotation } - else NoDenotation } if (this eq that) this @@ -333,7 +335,7 @@ object Denotations { def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = { def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation = - if (denot1.signature matches denot2.signature) { + if (denot1.matches(denot2)) { val sym1 = denot1.symbol val sym2 = denot2.symbol val info1 = denot1.info @@ -472,6 +474,9 @@ object Denotations { if (sig matches situated.signature) this else NoDenotation } + def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = + signature.matches(other.signature) && info.matches(other.info) + // ------ Forming types ------------------------------------------- /** The TypeRef representing this type denotation at its original location. */ diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 8376dd4e9..9092523db 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -312,7 +312,7 @@ trait Checking { def checkDecl(decl: Symbol): Unit = { for (other <- seen(decl.name)) { typr.println(i"conflict? $decl $other") - if (decl.signature matches other.signature) { + if (decl.matches(other)) { def doubleDefError(decl: Symbol, other: Symbol): Unit = { def ofType = if (decl.isType) "" else d": ${other.info}" def explanation = diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1ae0bec0c..3247592c3 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -63,7 +63,8 @@ trait TypeAssigner { val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) def addRefinement(parent: Type, decl: Symbol) = { val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) - val inheritedInfo = inherited.atSignature(decl.info .signature).info + val inheritedInfo = inherited.atSignature(decl.info.signature).info + // @smarter atSignature probably wrong now; we are now missing out on types that refine the result type if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) typr.echo( i"add ref $parent $decl --> ", diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 403a99a19..2b5b86be1 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,7 +104,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 = 12)(allowDoubleBindings) + @Test def neg_typers() = compileFile(negDir, "typers", xerrors = 10)(allowDoubleBindings) @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) @@ -115,7 +115,6 @@ class tests extends CompilerTest { @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 11) @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 2) @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 6) - @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) @Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12) val negTailcallDir = negDir + "tailcall/" diff --git a/tests/neg/typers.scala b/tests/neg/typers.scala index b5bd1fa2c..9fcc63e38 100644 --- a/tests/neg/typers.scala +++ b/tests/neg/typers.scala @@ -22,11 +22,11 @@ object typers { val z: Int def z(): String // error: double def - def f(x: Any) = () // error: double def + def f(x: Any) = () // OK! def f(x: AnyRef): AnyRef def g(x: Object): Unit - def g[T](x: T): T = x // error: double def + def g[T](x: T): T = x // OK! } -- cgit v1.2.3