From 42b89dcd3d21efcae9dd96726e6ad0148c8af83c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 14 Nov 2020 10:53:06 +0100 Subject: [PATCH 1/2] Fix #10311: Inaccessible members do not qualify for SelectionProtos --- .../dotty/tools/dotc/typer/ProtoTypes.scala | 10 ++++--- tests/neg/overloading-specifity.scala | 3 ++- tests/pos/i10311.scala | 15 +++++++++++ tests/run/overloading-specifity-2.scala | 27 +++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i10311.scala create mode 100644 tests/run/overloading-specifity-2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 71d99a210f7d..8cf76fdce844 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -180,10 +180,12 @@ object ProtoTypes { { val mbr = if (privateOK) tp1.member(name) else tp1.nonPrivateMember(name) def qualifies(m: SingleDenotation) = - memberProto.isRef(defn.UnitClass) || - tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto, keepConstraint) - // Note: can't use `m.info` here because if `m` is a method, `m.info` - // loses knowledge about `m`'s default arguments. + val isAccessible = !m.symbol.exists || m.symbol.isAccessibleFrom(tp1, superAccess = true) + isAccessible + && (memberProto.isRef(defn.UnitClass) + || tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto, keepConstraint)) + // Note: can't use `m.info` here because if `m` is a method, `m.info` + // loses knowledge about `m`'s default arguments. mbr match { // hasAltWith inlined for performance case mbr: SingleDenotation => mbr.exists && qualifies(mbr) case _ => mbr hasAltWith qualifies diff --git a/tests/neg/overloading-specifity.scala b/tests/neg/overloading-specifity.scala index 840bc322d7b5..89ed440bdaba 100644 --- a/tests/neg/overloading-specifity.scala +++ b/tests/neg/overloading-specifity.scala @@ -23,5 +23,6 @@ object Test extends App { def foo[T]: Show[T] = new Show[T](2) } - assert(a.foo[Int].i == 2) // error: no implicit argument of type Test.Context was found for parameter ctx + val b = a.foo[Int] // error: no implicit argument of type context found + assert(b.i == 1) // error } \ No newline at end of file diff --git a/tests/pos/i10311.scala b/tests/pos/i10311.scala new file mode 100644 index 000000000000..bfa0c15f3493 --- /dev/null +++ b/tests/pos/i10311.scala @@ -0,0 +1,15 @@ +object Module { + class MyInt(private val x: Int, private val y: Int) + object MyInt { + implicit class Ops(self: MyInt) extends AnyVal { + def x: Int = self.x + } + extension (self: MyInt) def y: Int = self.y + } +} +object test: + import Module._ + + val a = new MyInt(42, 43) + val b = a.x + val c = a.y diff --git a/tests/run/overloading-specifity-2.scala b/tests/run/overloading-specifity-2.scala new file mode 100644 index 000000000000..13ff783d9535 --- /dev/null +++ b/tests/run/overloading-specifity-2.scala @@ -0,0 +1,27 @@ +// Shows that overloading resolution does not test implicits to decide +// applicability. A best alternative is picked first, and then implicits +// are searched for this one. +case class Show[T](val i: Int) +class Show1[T](i: Int) extends Show[T](i) + +class Generic +object Generic { + implicit val gen: Generic = new Generic + implicit def showGen[T](implicit gen: Generic): Show[T] = new Show[T](2) +} + +object Test extends App { + trait Context + //given ctx as Context + + object a { + def foo[T](implicit gen: Generic): Show[T] = new Show[T](1) + def foo[T](implicit gen: Generic, ctx: Context): Show1[T] = new Show1[T](2) + } + object b { + def foo[T](implicit gen: Generic): Show[T] = new Show[T](1) + def foo[T]: Show[T] = new Show[T](2) + } + + assert(a.foo[Int].i == 1) // error: no implicit argument of type Test.Context was found for parameter ctx +} \ No newline at end of file From e4494c338a0bd6053ae03fb3a19d0c8506e8810c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 14 Nov 2020 10:53:57 +0100 Subject: [PATCH 2/2] Drop unused attachment --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 5 ----- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 402e0d6d7e14..603d421bf150 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -23,11 +23,6 @@ object desugar { import untpd._ import DesugarEnums._ - /** If a Select node carries this attachment, suppress the check - * that its type refers to an acessible symbol. - */ - val SuppressAccessCheck: Property.Key[Unit] = Property.Key() - /** An attachment for companion modules of classes that have a `derives` clause. * The position value indicates the start position of the template of the * deriving class. diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ad4a6c73cd88..2787a62509af 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -173,11 +173,9 @@ trait TypeAssigner { /** The type of the selection in `tree`, where `qual1` is the typed qualifier part. * The selection type is additionally checked for accessibility. */ - def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(using Context): Type = { + def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(using Context): Type = val ownType = selectionType(tree, qual1) - if (tree.hasAttachment(desugar.SuppressAccessCheck)) ownType - else ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.srcPos) - } + ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.srcPos) /** Type assignment method. Each method takes as parameters * - an untpd.Tree to which it assigns a type,