From c878f8101173d27fe9640bea5d1cea704061ca3c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 12 Mar 2017 13:38:50 +0100 Subject: Fix #2066: Don't qualify private members in SelectionProto's... ... unless they would be accessible in the given context. --- tests/pos/i2066.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/pos/i2066.scala (limited to 'tests') diff --git a/tests/pos/i2066.scala b/tests/pos/i2066.scala new file mode 100644 index 000000000..505e5e334 --- /dev/null +++ b/tests/pos/i2066.scala @@ -0,0 +1,15 @@ +class Foo + +object Test { + implicit class One(x: Foo) { + def meth: Unit = {} + } + + implicit class Two(x: Foo) { + private def meth: Unit = {} + } + + def test(foo: Foo): Unit = { + foo.meth + } +} -- cgit v1.2.3 From 32617344f90001faa65a020f54d309d076da8fcc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 12 Mar 2017 18:09:48 +0100 Subject: Alternative fix of #2066. Now we never match `? { name: T }` with types that have only a private `name` member. This is what scalac does, too. --- .../src/dotty/tools/dotc/typer/Implicits.scala | 10 ++++++-- .../src/dotty/tools/dotc/typer/ProtoTypes.scala | 28 ++++++++++++---------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/neg/i2066.scala | 27 +++++++++++++++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 tests/neg/i2066.scala (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index ebbcbcc95..681045cc4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -516,8 +516,13 @@ trait Implicits { self: Typer => || (from.tpe isRef defn.NullClass) || !(ctx.mode is Mode.ImplicitsEnabled) || (from.tpe eq NoPrefix)) NoImplicitMatches - else - try inferImplicit(to.stripTypeVar.widenExpr, from, from.pos) + else { + def adjust(to: Type) = to.stripTypeVar.widenExpr match { + case SelectionProto(name, memberProto, compat, true) => + SelectionProto(name, memberProto, compat, privateOK = false) + case tp => tp + } + try inferImplicit(adjust(to), from, from.pos) catch { case ex: AssertionError => implicits.println(s"view $from ==> $to") @@ -525,6 +530,7 @@ trait Implicits { self: Typer => implicits.println(TypeComparer.explained(implicit ctx => from.tpe <:< to)) throw ex } + } } /** Find an implicit argument for parameter `formal`. diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index f67798eaa..b588e3ae5 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -90,14 +90,12 @@ object ProtoTypes { * * [ ].name: proto */ - abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility) + abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean) extends CachedProxyType with ProtoType with ValueTypeOrProto { override def isMatchedBy(tp1: Type)(implicit ctx: Context) = { name == nme.WILDCARD || { - val mbr = - if (tp1.widen.classSymbol.isLinkedWith(ctx.owner.enclosingClass)) tp1.member(name) - else tp1.nonPrivateMember(name) + val mbr = if (privateOK) tp1.member(name) else tp1.nonPrivateMember(name) def qualifies(m: SingleDenotation) = memberProto.isRef(defn.UnitClass) || compat.normalizedCompatible(m.info, memberProto) @@ -112,11 +110,11 @@ object ProtoTypes { def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) = if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this - else SelectionProto(name, memberProto, compat) + else SelectionProto(name, memberProto, compat, privateOK) override def equals(that: Any): Boolean = that match { case that: SelectionProto => - (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) + (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) && (privateOK == that.privateOK) case _ => false } @@ -126,14 +124,18 @@ object ProtoTypes { override def deepenProto(implicit ctx: Context) = derivedSelectionProto(name, memberProto.deepenProto, compat) - override def computeHash = addDelta(doHash(name, memberProto), if (compat eq NoViewsAllowed) 1 else 0) + override def computeHash = { + val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0) + addDelta(doHash(name, memberProto), delta) + } } - class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat) + class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean) + extends SelectionProto(name, memberProto, compat, privateOK) object SelectionProto { - def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = { - val selproto = new CachedSelectionProto(name, memberProto, compat) + def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)(implicit ctx: Context): SelectionProto = { + val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK) if (compat eq NoViewsAllowed) unique(selproto) else selproto } } @@ -145,7 +147,7 @@ object ProtoTypes { if (name.isConstructorName) WildcardType else tp match { case tp: UnapplyFunProto => new UnapplySelectionProto(name) - case tp => SelectionProto(name, IgnoredProto(tp), typer) + case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true) } /** A prototype for expressions [] that are in some unspecified selection operation @@ -156,10 +158,10 @@ object ProtoTypes { * operation is further selection. In this case, the expression need not be a value. * @see checkValue */ - @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed) + @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true) /** A prototype for selections in pattern constructors */ - class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed) + class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed, true) trait ApplyingProto extends ProtoType diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ba14b7498..2578da649 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1714,7 +1714,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") { tree match { case Select(qual, name) => - val qualProto = SelectionProto(name, pt, NoViewsAllowed) + val qualProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false) tryEither { implicit ctx => val qual1 = adaptInterpolated(qual, qualProto, EmptyTree) if ((qual eq qual1) || ctx.reporter.hasErrors) None diff --git a/tests/neg/i2066.scala b/tests/neg/i2066.scala new file mode 100644 index 000000000..0081cec07 --- /dev/null +++ b/tests/neg/i2066.scala @@ -0,0 +1,27 @@ +class Foo + +object Test { + implicit def two(x: Foo): Two = new Two(x) + + class Two(x: Foo) { + private def meth: Unit = {} + + def test2(foo: Foo): Unit = { + foo.meth // error + } + } +} + + +object Test2 { + + class Two(x: Foo) { + implicit def two(x: Foo): Two = new Two(x) + + private def meth: Unit = {} + + def test2(foo: Foo): Unit = { + foo.meth // error + } + } +} -- cgit v1.2.3