diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 4c2e629ff112..8f0ae7a2da93 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -403,7 +403,7 @@ object Denotations { } case denot1: SingleDenotation => if (denot1 eq denot2) denot1 - else if (denot1.matches(denot2)) mergeSingleDenot(denot1, denot2) + else if denot1.matches(denot2) then mergeSingleDenot(denot1, denot2) else NoDenotation } @@ -438,8 +438,11 @@ object Denotations { else defn.RootClass) def isHidden(sym: Symbol) = sym.exists && !sym.isAccessibleFrom(pre) - val hidden1 = isHidden(sym1) - val hidden2 = isHidden(sym2) + // In typer phase filter out denotations with symbols that are not + // accessible. After typer, this is not possible since we cannot guarantee + // that the current owner is set correctly. See pos/14660.scala. + val hidden1 = isHidden(sym1) && ctx.isTyper + val hidden2 = isHidden(sym2) && ctx.isTyper if hidden1 && !hidden2 then denot2 else if hidden2 && !hidden1 then denot1 else diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5c1e1757de52..960a660e0aea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -537,6 +537,7 @@ object Checking { checkCombination(Private, Protected) checkCombination(Abstract, Override) checkCombination(Private, Override) + if sym.isType && !sym.isClass then checkCombination(Private, Opaque) checkCombination(Lazy, Inline) // The issue with `erased inline` is that the erased semantics get lost // as the code is inlined and the reference is removed before the erased usage check. diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 76bf3ec48728..daa73c0481da 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -441,7 +441,7 @@ object QuoteMatcher { } /** Result of matching a part of an expression */ - private opaque type Matching = Option[Tuple] + private type Matching = Option[Tuple] private object Matching { diff --git a/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index 0a992ff5f6be..83608ca78dd3 100644 --- a/docs/_docs/reference/other-new-features/opaques-details.md +++ b/docs/_docs/reference/other-new-features/opaques-details.md @@ -51,6 +51,8 @@ object o: def id(x: o.T): o.T = x ``` +Opaque type aliases cannot be `private` and cannot be overridden in subclasses. + ## Type Parameters of Opaque Types Opaque type aliases can have a single type parameter list. The following aliases diff --git a/tests/neg/i14660.scala b/tests/neg/i14660.scala new file mode 100644 index 000000000000..b7b45a25d972 --- /dev/null +++ b/tests/neg/i14660.scala @@ -0,0 +1,8 @@ +class Bar: + private opaque type Baz = Int // error + + private object Foo: + opaque type O = Int // OK + + val x: Baz = 1 + diff --git a/tests/pos/i14660.scala b/tests/pos/i14660.scala new file mode 100644 index 000000000000..b8ee3b5b86b0 --- /dev/null +++ b/tests/pos/i14660.scala @@ -0,0 +1,6 @@ +trait Foo: + class Bar: + private[Foo] opaque type Baz = Int + + def foo: Bar#Baz +