Skip to content

Commit 6c3b474

Browse files
authored
Merge pull request #6721 from dotty-staging/fix-opaque
Handle opaque types in computeAsSeenFrom
2 parents 822e759 + 8a7522c commit 6c3b474

File tree

5 files changed

+56
-18
lines changed

5 files changed

+56
-18
lines changed

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,8 +1118,31 @@ object Denotations {
11181118
case thisd: SymDenotation => thisd.owner
11191119
case _ => if (symbol.exists) symbol.owner else NoSymbol
11201120
}
1121-
if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this
1122-
else derivedSingleDenotation(symbol, symbol.info.asSeenFrom(pre, owner))
1121+
def derived(info: Type) = derivedSingleDenotation(symbol, info.asSeenFrom(pre, owner))
1122+
pre match {
1123+
case pre: ThisType if symbol.isOpaqueAlias && pre.cls == symbol.owner =>
1124+
// This code is necessary to compensate for a "window of vulnerability" with
1125+
// opaque types. The problematic sequence is as follows.
1126+
// 1. Type a selection `m.this.T` where `T` is an opaque type alias in `m`
1127+
// and this is the first access
1128+
// 2. `T` will normalize to an abstract type on completion.
1129+
// 3. At that time, the default logic in the second case is wrong: `T`'s new info
1130+
// is now an abstract type and running it through an asSeenFrom gives nothing.
1131+
// We fix this as follows:
1132+
// 1. Force opaque normalization as first step
1133+
// 2. Read the info from the enclosing object's refinement
1134+
symbol.normalizeOpaque()
1135+
def findRefined(tp: Type, name: Name): Type = tp match {
1136+
case RefinedType(parent, rname, rinfo) =>
1137+
if (rname == name) rinfo else findRefined(parent, name)
1138+
case _ =>
1139+
symbol.info
1140+
}
1141+
derived(findRefined(pre.underlying, symbol.name))
1142+
case _ =>
1143+
if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this
1144+
else derived(symbol.info)
1145+
}
11231146
}
11241147

11251148
/** Does this denotation have all the `required` flags but none of the `excluded` flags?

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,10 +1891,8 @@ object Types {
18911891
finish(memberDenot(symd.initial.name, allowPrivate = false))
18921892
else if (prefix.isArgPrefixOf(symd))
18931893
finish(argDenot(sym.asType))
1894-
else if (infoDependsOnPrefix(symd, prefix)) {
1895-
if (!symd.isClass && symd.is(Opaque, butNot = Deferred)) symd.normalizeOpaque()
1894+
else if (infoDependsOnPrefix(symd, prefix))
18961895
finish(memberDenot(symd.initial.name, allowPrivate = symd.is(Private)))
1897-
}
18981896
else
18991897
finish(symd.current)
19001898
}

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
317317
case _ => tree
318318
}
319319

320+
def importText(deleg: Boolean, expr: Tree, selectors: List[Tree]) = {
321+
def selectorText(sel: Tree): Text = sel match {
322+
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
323+
case _: Ident => toTextGlobal(sel)
324+
case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt)
325+
}
326+
val selectorsText: Text = selectors match {
327+
case id :: Nil => toText(id)
328+
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
329+
}
330+
(keywordText("delegate ") provided deleg) ~
331+
toTextLocal(expr) ~ "." ~ selectorsText
332+
}
333+
320334
tree match {
321335
case id: Trees.BackquotedIdent[_] if !homogenizedView =>
322336
"`" ~ toText(id.name) ~ "`"
@@ -499,18 +513,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
499513
typeDefText(tparamsTxt, optText(rhs)(" = " ~ _))
500514
}
501515
recur(rhs, "")
502-
case Import(importImplied, expr, selectors) =>
503-
def selectorText(sel: Tree): Text = sel match {
504-
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
505-
case _: Ident => toTextGlobal(sel)
506-
case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt)
507-
}
508-
val selectorsText: Text = selectors match {
509-
case id :: Nil => toText(id)
510-
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
511-
}
512-
keywordText("import ") ~ (keywordText("delegate ") provided importImplied) ~
513-
toTextLocal(expr) ~ "." ~ selectorsText
516+
case Import(deleg, expr, selectors) =>
517+
keywordText("import ") ~ importText(deleg, expr, selectors)
518+
case Export(deleg, expr, selectors) =>
519+
keywordText("export ") ~ importText(deleg, expr, selectors)
514520
case packageDef: PackageDef =>
515521
packageDefText(packageDef)
516522
case tree: Template =>

tests/pos/export-opaque.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object A {
2+
3+
object opaques {
4+
opaque type FlagSet = Long
5+
def FlagSet(bits: Long): FlagSet = bits
6+
}
7+
//type FlagSet = opaques.FlagSet
8+
//def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits)
9+
export opaques.FlagSet
10+
11+
}

tests/pos/implicit-scope.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object A {
22

33
object opaques {
44
opaque type FlagSet = Long
5-
def FlagSet(bits: Long): FlagSet = bits.asInstanceOf // !!!
5+
def FlagSet(bits: Long): FlagSet = bits
66
def toBits(fs: FlagSet): Long = fs
77
}
88
val someFlag = FlagSet(1)

0 commit comments

Comments
 (0)