Skip to content

Commit

Permalink
Merge pull request #13857 from dotty-staging/fix-#13851
Browse files Browse the repository at this point in the history
Detect opaque aliases in inline val types
  • Loading branch information
nicolasstucki committed Nov 2, 2021
2 parents 7e45696 + 9b549ab commit 9b3e6c4
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 13 deletions.
29 changes: 18 additions & 11 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1363,42 +1363,49 @@ object Types {
case Atoms.Unknown => Atoms.Unknown
case _ => Atoms.Unknown

private def dealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = this match {
private def dealias1(keep: AnnotatedType => Context ?=> Boolean, keepOpaques: Boolean)(using Context): Type = this match {
case tp: TypeRef =>
if (tp.symbol.isClass) tp
else tp.info match {
case TypeAlias(alias) => alias.dealias1(keep)
case TypeAlias(alias) if !(keepOpaques && tp.symbol.is(Opaque)) =>
alias.dealias1(keep, keepOpaques)
case _ => tp
}
case app @ AppliedType(tycon, _) =>
val tycon1 = tycon.dealias1(keep)
if (tycon1 ne tycon) app.superType.dealias1(keep)
val tycon1 = tycon.dealias1(keep, keepOpaques)
if (tycon1 ne tycon) app.superType.dealias1(keep, keepOpaques)
else this
case tp: TypeVar =>
val tp1 = tp.instanceOpt
if (tp1.exists) tp1.dealias1(keep) else tp
if (tp1.exists) tp1.dealias1(keep, keepOpaques) else tp
case tp: AnnotatedType =>
val tp1 = tp.parent.dealias1(keep)
val tp1 = tp.parent.dealias1(keep, keepOpaques)
if keep(tp) then tp.derivedAnnotatedType(tp1, tp.annot) else tp1
case tp: LazyRef =>
tp.ref.dealias1(keep)
tp.ref.dealias1(keep, keepOpaques)
case _ => this
}

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

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

/** Like `dealiasKeepAnnots`, but keeps only refining annotations */
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining)
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining, keepOpaques = false)

/** Follow non-opaque aliases and dereferences LazyRefs, annotated types and instantiated
* TypeVars until type is no longer alias type, annotated type, LazyRef,
* or instantiated type variable.
*/
final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true)

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

private def widenDealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = {
val res = this.widen.dealias1(keep)
val res = this.widen.dealias1(keep, keepOpaques = false)
if (res eq this) res else res.widenDealias1(keep)
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/InlineVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ class InlineVals extends MiniPhase:
then
val rhs = tree.rhs
val tpt = tree.tpt
tpt.tpe.widenTermRefExpr.dealias.normalized match
tpt.tpe.widenTermRefExpr.dealiasKeepOpaques.normalized match
case tp: ConstantType =>
if !isPureExpr(rhs) then
val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs"
report.error(s"inline value must be pure$details", rhs.srcPos)
case tp =>
if tp.derivesFrom(defn.UnitClass) then
if tp.typeSymbol.is(Opaque) then
report.error(em"The type of an `inline val` cannot be an opaque type.\n\nTo inline, consider using `inline def` instead", rhs)
else if tp.derivesFrom(defn.UnitClass) then
report.error(em"`inline val` of type `Unit` is not supported.\n\nTo inline a `Unit` consider using `inline def`", rhs)
else if tp.derivesFrom(defn.StringClass) || defn.ScalaValueClasses().exists(tp.derivesFrom(_)) then
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos
Expand Down
11 changes: 11 additions & 0 deletions tests/neg/i13851.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
opaque type One = 1
inline val One: One = 1 // error

opaque type Max = Int.MaxValue.type
inline val Max: Max = Int.MaxValue // error

inline val MaxValue: Int.MaxValue.type = Int.MaxValue

opaque type Two = 2
type Bis = Two
inline val Two: Bis = 2 // error
10 changes: 10 additions & 0 deletions tests/neg/i13851b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
object Num {
opaque type One = 1
inline val One: One = 1 // error

opaque type Two = 2
inline def Two: Two = 2
}

def test1 = Num.One
def test2 = Num.Two
6 changes: 6 additions & 0 deletions tests/neg/i13852.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
inline val `1`: 1 = 1
def get1: 1 = `1`

opaque type One = 1
inline val One: One = 1 // error
def getOne: One = One

0 comments on commit 9b3e6c4

Please sign in to comment.