From 8c8e03e13653ae9c62f811fdfd9ebb7b819aab6e Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Mar 2022 10:10:40 +0100 Subject: [PATCH 1/5] Disallow private opaque type aliases Fixes #14660 --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 1 + docs/_docs/reference/other-new-features/opaques-details.md | 2 ++ tests/neg/i14660.scala | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 tests/neg/i14660.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5c1e1757de52..3aeaa9e659b6 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) + 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/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index 0a992ff5f6be..d5f868795173 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 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..998092503ccf --- /dev/null +++ b/tests/neg/i14660.scala @@ -0,0 +1,2 @@ +class Bar: + private opaque type Baz = Int // error From 1ffbe2a1d9142e712fb16798e1b86b88d0841e39 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Mar 2022 10:14:02 +0100 Subject: [PATCH 2/5] Update docs/_docs/reference/other-new-features/opaques-details.md Co-authored-by: Jamie Thompson --- docs/_docs/reference/other-new-features/opaques-details.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index d5f868795173..83608ca78dd3 100644 --- a/docs/_docs/reference/other-new-features/opaques-details.md +++ b/docs/_docs/reference/other-new-features/opaques-details.md @@ -51,7 +51,7 @@ object o: def id(x: o.T): o.T = x ``` -Opaque type aliases cannot be `private` and cannot overridden in subclasses. +Opaque type aliases cannot be `private` and cannot be overridden in subclasses. ## Type Parameters of Opaque Types From 00676e31abb679fbbaf0ef40ad761e80b28bca3d Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Mar 2022 10:27:37 +0100 Subject: [PATCH 3/5] Drop opaque in Matching A private opaque type alias is equivalent to a private type alias --- compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From b6d815ec7b854cfc60ee06398a7614843ec9f80f Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Mar 2022 13:06:22 +0100 Subject: [PATCH 4/5] Allow private objects containing opaque types --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- tests/neg/i14660.scala | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 3aeaa9e659b6..960a660e0aea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -537,7 +537,7 @@ object Checking { checkCombination(Private, Protected) checkCombination(Abstract, Override) checkCombination(Private, Override) - checkCombination(Private, Opaque) + 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/tests/neg/i14660.scala b/tests/neg/i14660.scala index 998092503ccf..b7b45a25d972 100644 --- a/tests/neg/i14660.scala +++ b/tests/neg/i14660.scala @@ -1,2 +1,8 @@ class Bar: private opaque type Baz = Int // error + + private object Foo: + opaque type O = Int // OK + + val x: Baz = 1 + From fe6c29997d87792b7200b04d72dd8f8382c3433c Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Mar 2022 19:38:16 +0100 Subject: [PATCH 5/5] Fix problem when merging denotations with hidden symbols The hidden test is unreliable after typer, since we cannot guarantee that the current owner is accurate. --- compiler/src/dotty/tools/dotc/core/Denotations.scala | 9 ++++++--- tests/pos/i14660.scala | 6 ++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14660.scala 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/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 +