Skip to content

Commit 9b3e6c4

Browse files
Merge pull request #13857 from dotty-staging/fix-#13851
Detect opaque aliases in inline val types
2 parents 7e45696 + 9b549ab commit 9b3e6c4

File tree

5 files changed

+49
-13
lines changed

5 files changed

+49
-13
lines changed

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

+18-11
Original file line numberDiff line numberDiff line change
@@ -1363,42 +1363,49 @@ object Types {
13631363
case Atoms.Unknown => Atoms.Unknown
13641364
case _ => Atoms.Unknown
13651365

1366-
private def dealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = this match {
1366+
private def dealias1(keep: AnnotatedType => Context ?=> Boolean, keepOpaques: Boolean)(using Context): Type = this match {
13671367
case tp: TypeRef =>
13681368
if (tp.symbol.isClass) tp
13691369
else tp.info match {
1370-
case TypeAlias(alias) => alias.dealias1(keep)
1370+
case TypeAlias(alias) if !(keepOpaques && tp.symbol.is(Opaque)) =>
1371+
alias.dealias1(keep, keepOpaques)
13711372
case _ => tp
13721373
}
13731374
case app @ AppliedType(tycon, _) =>
1374-
val tycon1 = tycon.dealias1(keep)
1375-
if (tycon1 ne tycon) app.superType.dealias1(keep)
1375+
val tycon1 = tycon.dealias1(keep, keepOpaques)
1376+
if (tycon1 ne tycon) app.superType.dealias1(keep, keepOpaques)
13761377
else this
13771378
case tp: TypeVar =>
13781379
val tp1 = tp.instanceOpt
1379-
if (tp1.exists) tp1.dealias1(keep) else tp
1380+
if (tp1.exists) tp1.dealias1(keep, keepOpaques) else tp
13801381
case tp: AnnotatedType =>
1381-
val tp1 = tp.parent.dealias1(keep)
1382+
val tp1 = tp.parent.dealias1(keep, keepOpaques)
13821383
if keep(tp) then tp.derivedAnnotatedType(tp1, tp.annot) else tp1
13831384
case tp: LazyRef =>
1384-
tp.ref.dealias1(keep)
1385+
tp.ref.dealias1(keep, keepOpaques)
13851386
case _ => this
13861387
}
13871388

13881389
/** Follow aliases and dereferences LazyRefs, annotated types and instantiated
13891390
* TypeVars until type is no longer alias type, annotated type, LazyRef,
13901391
* or instantiated type variable.
13911392
*/
1392-
final def dealias(using Context): Type = dealias1(keepNever)
1393+
final def dealias(using Context): Type = dealias1(keepNever, keepOpaques = false)
13931394

13941395
/** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
13951396
* is no longer alias type, LazyRef, or instantiated type variable.
13961397
* Goes through annotated types and rewraps annotations on the result.
13971398
*/
1398-
final def dealiasKeepAnnots(using Context): Type = dealias1(keepAlways)
1399+
final def dealiasKeepAnnots(using Context): Type = dealias1(keepAlways, keepOpaques = false)
13991400

14001401
/** Like `dealiasKeepAnnots`, but keeps only refining annotations */
1401-
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining)
1402+
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining, keepOpaques = false)
1403+
1404+
/** Follow non-opaque aliases and dereferences LazyRefs, annotated types and instantiated
1405+
* TypeVars until type is no longer alias type, annotated type, LazyRef,
1406+
* or instantiated type variable.
1407+
*/
1408+
final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true)
14021409

14031410
/** Approximate this type with a type that does not contain skolem types. */
14041411
final def deskolemized(using Context): Type =
@@ -1426,7 +1433,7 @@ object Types {
14261433
def tryNormalize(using Context): Type = NoType
14271434

14281435
private def widenDealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = {
1429-
val res = this.widen.dealias1(keep)
1436+
val res = this.widen.dealias1(keep, keepOpaques = false)
14301437
if (res eq this) res else res.widenDealias1(keep)
14311438
}
14321439

compiler/src/dotty/tools/dotc/transform/InlineVals.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ class InlineVals extends MiniPhase:
3333
then
3434
val rhs = tree.rhs
3535
val tpt = tree.tpt
36-
tpt.tpe.widenTermRefExpr.dealias.normalized match
36+
tpt.tpe.widenTermRefExpr.dealiasKeepOpaques.normalized match
3737
case tp: ConstantType =>
3838
if !isPureExpr(rhs) then
3939
val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs"
4040
report.error(s"inline value must be pure$details", rhs.srcPos)
4141
case tp =>
42-
if tp.derivesFrom(defn.UnitClass) then
42+
if tp.typeSymbol.is(Opaque) then
43+
report.error(em"The type of an `inline val` cannot be an opaque type.\n\nTo inline, consider using `inline def` instead", rhs)
44+
else if tp.derivesFrom(defn.UnitClass) then
4345
report.error(em"`inline val` of type `Unit` is not supported.\n\nTo inline a `Unit` consider using `inline def`", rhs)
4446
else if tp.derivesFrom(defn.StringClass) || defn.ScalaValueClasses().exists(tp.derivesFrom(_)) then
4547
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos

tests/neg/i13851.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
opaque type One = 1
2+
inline val One: One = 1 // error
3+
4+
opaque type Max = Int.MaxValue.type
5+
inline val Max: Max = Int.MaxValue // error
6+
7+
inline val MaxValue: Int.MaxValue.type = Int.MaxValue
8+
9+
opaque type Two = 2
10+
type Bis = Two
11+
inline val Two: Bis = 2 // error

tests/neg/i13851b.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Num {
2+
opaque type One = 1
3+
inline val One: One = 1 // error
4+
5+
opaque type Two = 2
6+
inline def Two: Two = 2
7+
}
8+
9+
def test1 = Num.One
10+
def test2 = Num.Two

tests/neg/i13852.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
inline val `1`: 1 = 1
2+
def get1: 1 = `1`
3+
4+
opaque type One = 1
5+
inline val One: One = 1 // error
6+
def getOne: One = One

0 commit comments

Comments
 (0)