Skip to content

Commit 9f3cac8

Browse files
committed
Allow into on opaque type aliases
1 parent 843ac25 commit 9f3cac8

File tree

9 files changed

+36
-23
lines changed

9 files changed

+36
-23
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ object desugar {
652652
tdef, evidenceBuf,
653653
(tdef.mods.flags.toTermFlags & AccessFlags) | Lazy | DeferredGivenFlags,
654654
inventGivenName, Nil)
655+
if tdef.mods.flags.is(Into, butNot = Opaque) then
656+
report.error(ModifierNotAllowedForDefinition(Into), flagSourcePos(tdef, Into))
655657
if evidenceBuf.isEmpty then result else Thicket(result :: evidenceBuf.toList)
656658

657659
/** The expansion of a class definition. See inline comments for what is involved */

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Annotations.Annotation
99
import NameKinds.ContextBoundParamName
1010
import typer.ConstFold
1111
import reporting.trace
12+
import util.SrcPos
1213

1314
import Decorators.*
1415
import Constants.Constant
@@ -522,6 +523,10 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
522523
if id.span == result.span.startPos => Some(result)
523524
case _ => None
524525
end ImpureByNameTypeTree
526+
527+
/** The position of the modifier associated with given flag in this definition. */
528+
def flagSourcePos(mdef: DefTree, flag: FlagSet): SrcPos =
529+
mdef.mods.mods.find(_.flags == flag).getOrElse(mdef).srcPos
525530
}
526531

527532
trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ object Flags {
547547
val RetainedExportTypeFlags = Infix
548548

549549
/** Flags that apply only to classes */
550-
val ClassOnlyFlags = Sealed | Open | Into | Abstract.toTypeFlags
550+
val ClassOnlyFlags = Sealed | Open | Abstract.toTypeFlags
551551

552552
// ------- Other flag sets -------------------------------------
553553

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ object Types extends TypeUtils {
458458
def isConversionTargetType(using Context): Boolean =
459459
dealias(KeepOpaques).match
460460
case tp: TypeRef =>
461-
tp.symbol.isClass && tp.symbol.is(Into)
461+
(tp.symbol.isClass || tp.symbol.isOpaqueAlias) && tp.symbol.is(Into)
462462
case tp @ AppliedType(tycon, _) =>
463463
isInto || tycon.isConversionTargetType
464464
case tp: AndOrType =>

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,7 @@ object Checking {
631631
*/
632632
def checkWellFormedModule(mdef: untpd.ModuleDef)(using Context) =
633633
val mods = mdef.mods
634-
def flagSourcePos(flag: FlagSet) =
635-
mods.mods.find(_.flags == flag).getOrElse(mdef).srcPos
634+
def flagSourcePos(flag: Flag) = untpd.flagSourcePos(mdef, flag)
636635
if mods.is(Open) then
637636
report.error(ModifierNotAllowedForDefinition(Open), flagSourcePos(Open))
638637
if mods.is(Into) then

docs/_docs/reference/experimental/into.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class without requiring a language import.
2020
The first scheme is
2121
to have a special type `into[T]` which serves as a marker that conversions into that type are allowed. These types are typically used in parameters of methods that are designed to work with implicit conversions of their arguments. This allows fine-grained control over where implicit conversions should be allowed. We call this scheme "_into as a type constructor_".
2222

23-
The second scheme allows `into` as a soft modifier on traits and classes. If a trait or class is declared with this modifier, conversions to that type are allowed. The second scheme requires that one has control over the conversion target types so that an `into` can be added to their declaration. It is appropriate where there are a few designated types that are meant to be conversion targets. If that's the case, migration from Scala 2 to Scala 3
23+
The second scheme allows `into` as a soft modifier on traits, classes, and opaque type aliases. If a type definition is declared with this modifier, conversions to that type are allowed. The second scheme requires that one has control over the conversion target types so that an `into` can be added to their declaration. It is appropriate where there are a few designated types that are meant to be conversion targets. If that's the case, migration from Scala 2 to Scala 3
2424
becomes easier since no function signatures need to be rewritten. We call this scheme "_into as a modifier_".
2525

2626

@@ -117,7 +117,7 @@ number of `IterableOnce[Char]` arguments, and also allows implicit conversions i
117117

118118
```scala
119119
def concatAll(xss: into[IterableOnce[Char]]*): List[Char] =
120-
xss.foldLeft(List[Char]())(_ ++ _)
120+
xss.foldRight(Nil)(_ ++: _)
121121
```
122122
Here, the call
123123
```scala
@@ -176,7 +176,7 @@ and then `++`, `flatMap` and other functions could use this alias in their param
176176
The `into` scheme discussed so far strikes a nice balance between explicitness and convenience. But migrating to it from Scala 2 implicits does require major changes since possibly a large number of function signatures has to be changed to allow conversions on the arguments. This might ultimately hold back migration to Scala 3 implicits.
177177

178178
To facilitate migration, we also introduce an alternative way to specify target types of implicit conversions. We allow `into` as a soft modifier on
179-
classes and traits. If a class or trait is declared with `into`, then implicit conversions into that class or trait don't need a language import.
179+
classes, traits, and opaque type aliases. If a type definition is declared with `into`, then implicit conversions into that type don't need a language import.
180180

181181
For instance, the Laminar framework
182182
defines a trait `Modifier` that is commonly used as a parameter type of user-defined methods and that should support implicit conversions into it.
@@ -205,17 +205,15 @@ The `into`-as-a-modifier scheme is handy in codebases that have a small set of s
205205

206206
To make the preceding descriptions more precise: An implicit conversion is permitted without an `implicitConversions` language import if the target type is a valid conversion target type. A valid conversion target type is one of the following:
207207

208-
- a type of the form `into[T]`,
209-
- a reference `p.C` to a class or trait `C` that is declared with an `into` modifier,
210-
which can also be followed by type arguments,
211-
- a type alias of a valid conversion target type,
212-
- a match type that reduces to a valid conversion target type,
213-
- an annotated type `T @ann` where `T` is a valid conversion target type,
214-
- a refined type `T {...}` where `T` is a valid conversion target type,
215-
- a union `T | U` of two valid conversion target types `T` and `U`,
216-
- an intersection `T & U` of two valid conversion target types `T` and `U`,
217-
- an instance of a type parameter that is explicitly instantiated to a valid conversion target type.
218-
208+
- A type of the form `into[T]`.
209+
- A reference `p.C` to a class, trait, or opaque type alias `C` that is declared with an `into` modifier. The reference can be followed by type arguments.
210+
- A type alias of a valid conversion target type.
211+
- A match type that reduces to a valid conversion target type.
212+
- An annotated type `T @ann` where `T` is a valid conversion target type.
213+
- A refined type `T {...}` where `T` is a valid conversion target type.
214+
- A union `T | U` of two valid conversion target types `T` and `U`.
215+
- An intersection `T & U` of two valid conversion target types `T` and `U`.
216+
- An instance of a type parameter that is explicitly instantiated to a valid conversion target type.
219217

220218
Type parameters that are not fully instantiated do not count as valid conversion target types. For instance, consider:
221219

@@ -283,5 +281,5 @@ of the original `Modifier` trait has changed. In summary, upgrading Laminar to u
283281
LocalModifier ::= ... | ‘into’
284282
```
285283

286-
`into` is a soft modifier. It is only allowed on traits and classes.
284+
`into` is a soft modifier. It is only allowed classes, traits, and opaque type aliases.
287285

tests/neg/into-mods.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
7 |into object M // error
77
|^^^^^^^^^^^^^
88
|Modifier into is not allowed for this definition
9+
-- [E156] Syntax Error: tests/neg/into-mods.scala:12:2 -----------------------------------------------------------------
10+
12 | into type T = Int // error
11+
| ^^^^
12+
| Modifier into is not allowed for this definition
913
-- Error: tests/neg/into-mods.scala:11:11 ------------------------------------------------------------------------------
1014
11 | into val x = 33 // error
1115
| ^^^^^^^^^^^^^^^
1216
| modifier(s) `into` incompatible with value definition
13-
-- Error: tests/neg/into-mods.scala:12:12 ------------------------------------------------------------------------------
14-
12 | into type T = Int // error
15-
| ^
16-
| only classes can be into

tests/neg/into-mods.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ object Test:
1010
into def foo = 22 // error
1111
into val x = 33 // error
1212
into type T = Int // error
13+
into opaque type U = Int // ok
1314

tests/warn/into-as-mod.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import Conversion.into
66
into trait T
77
class C(x: Int) extends T
88

9+
object K:
10+
into opaque type A = C
11+
given Conversion[Int, A] = C(_)
12+
913
object Test:
1014
given Conversion[Int, C] = C(_)
1115

@@ -14,6 +18,10 @@ object Test:
1418
f(1) // ok
1519
g(1) // warn
1620

21+
import K.*
22+
def h(x: A) = ()
23+
h(1)
24+
1725
into class Keyword(str: String)
1826
given stringToKeyword: Conversion[String, Keyword] = Keyword(_)
1927

0 commit comments

Comments
 (0)