Skip to content

Commit 80fcb70

Browse files
committed
A relaxation concerning exported type aliases
The rules for export forwarders are changed as follows. Previously, all export forwarders were declared `final`. Now, only term members are declared `final`. Type aliases left aside. This makes it possible to export the same type member into several traits and then mix these traits in the same class. `typeclass-aggregates.scala` shows why this is essential to be able to combine multiple givens with type members. The change does not lose safety since different type aliases would in any case lead to uninstantiatable classes.
1 parent edf341a commit 80fcb70

File tree

4 files changed

+63
-5
lines changed

4 files changed

+63
-5
lines changed

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ object Feature:
3333
val pureFunctions = experimental("pureFunctions")
3434
val captureChecking = experimental("captureChecking")
3535
val into = experimental("into")
36+
val modularity = experimental("modularity")
3637

3738
val globalOnlyImports: Set[TermName] = Set(pureFunctions, captureChecking)
3839

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Inferencing.*
2525
import transform.ValueClasses.*
2626
import TypeErasure.erasure
2727
import reporting.*
28-
import config.Feature.sourceVersion
28+
import config.Feature.{sourceVersion, modularity}
2929
import config.SourceVersion.*
3030

3131
import scala.compiletime.uninitialized
@@ -1191,7 +1191,7 @@ class Namer { typer: Typer =>
11911191
target = target.etaExpand(target.typeParams)
11921192
newSymbol(
11931193
cls, forwarderName,
1194-
Exported | Final,
1194+
Exported | (if Feature.enabled(modularity) then EmptyFlags else Final),
11951195
TypeAlias(target),
11961196
coord = span)
11971197
// Note: This will always create unparameterzied aliases. So even if the original type is

docs/_docs/reference/other-new-features/export.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ final def print(bits: BitMap): Unit = printUnit.print(bits)
3737
final type PrinterType = printUnit.PrinterType
3838
```
3939

40-
They can be accessed inside `Copier` as well as from outside:
40+
With the experimental `modularity` language import, only exported methods and values are final, whereas the generated `PrinterType` would be a simple type alias
41+
```scala
42+
type PrinterType = printUnit.PrinterType
43+
```
44+
45+
These aliases can be accessed inside `Copier` as well as from outside:
4146

4247
```scala
4348
val copier = new Copier
@@ -90,12 +95,17 @@ export O.*
9095
```
9196

9297
Export aliases copy the type and value parameters of the members they refer to.
93-
Export aliases are always `final`. Aliases of given instances are again defined as givens (and aliases of old-style implicits are `implicit`). Aliases of extensions are again defined as extensions. Aliases of inline methods or values are again defined `inline`. There are no other modifiers that can be given to an alias. This has the following consequences for overriding:
98+
Export aliases of term members are always `final`. Aliases of given instances are again defined as givens (and aliases of old-style implicits are `implicit`). Aliases of extensions are again defined as extensions. Aliases of inline methods or values are again defined `inline`. There are no other modifiers that can be given to an alias. This has the following consequences for overriding:
9499

95-
- Export aliases cannot be overridden, since they are final.
100+
- Export aliases of methods or fields cannot be overridden, since they are final.
96101
- Export aliases cannot override concrete members in base classes, since they are
97102
not marked `override`.
98103
- However, export aliases can implement deferred members of base classes.
104+
- Export type aliases are normally also final, except when the experimental
105+
language import `modularity` is present. The general
106+
rules for type aliases ensure in any case that if there are several type aliases in a class,
107+
they must agree on their right hand sides, or the class could not be instantiated.
108+
So dropping the `final` for export type aliases is safe.
99109

100110
Export aliases for public value definitions that are accessed without
101111
referring to private values in the qualifier path

tests/pos/typeclass-aggregates.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//> using options -source future -language:experimental.modularity
2+
trait Ord:
3+
type This
4+
extension (x: This)
5+
def compareTo(y: This): Int
6+
def < (y: This): Boolean = compareTo(y) < 0
7+
def > (y: This): Boolean = compareTo(y) > 0
8+
9+
trait OrdProxy extends Ord:
10+
export Ord.this.*
11+
12+
trait SemiGroup:
13+
type This
14+
extension (x: This) def combine(y: This): This
15+
16+
trait SemiGroupProxy extends SemiGroup:
17+
export SemiGroup.this.*
18+
19+
trait Monoid extends SemiGroup:
20+
def unit: This
21+
22+
trait MonoidProxy extends Monoid:
23+
export Monoid.this.*
24+
25+
def ordWithMonoid(ord: Ord, monoid: Monoid{ type This = ord.This }): Ord & Monoid =
26+
new ord.OrdProxy with monoid.MonoidProxy {}
27+
28+
trait OrdWithMonoid extends Ord, Monoid
29+
30+
def ordWithMonoid2(ord: Ord, monoid: Monoid{ type This = ord.This }) = //: OrdWithMonoid { type This = ord.This} =
31+
new OrdWithMonoid with ord.OrdProxy with monoid.MonoidProxy {}
32+
33+
given intOrd: Ord { type This = Int } = ???
34+
given intMonoid: Monoid { type This = Int } = ???
35+
36+
//given (using ord: Ord, monoid: Monoid{ type This = ord.This }): (Ord & Monoid { type This = ord.This}) =
37+
// ordWithMonoid2(ord, monoid)
38+
39+
val x = summon[Ord & Monoid { type This = Int}]
40+
val y: Int = ??? : x.This
41+
42+
// given [A, B](using ord: A is Ord, monoid: A is Monoid) => A is Ord & Monoid =
43+
// new ord.OrdProxy with monoid.MonoidProxy {}
44+
45+
given [A](using ord: Ord { type This = A }, monoid: Monoid { type This = A}): (Ord & Monoid) { type This = A} =
46+
new ord.OrdProxy with monoid.MonoidProxy {}
47+

0 commit comments

Comments
 (0)