Skip to content

Commit 71847a4

Browse files
committed
Use intersection to type selections
1 parent c83751d commit 71847a4

File tree

7 files changed

+86
-25
lines changed

7 files changed

+86
-25
lines changed

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import core.*
66
import Symbols.*, Contexts.*, Types.*, ContextOps.*, Decorators.*, SymDenotations.*
77
import Flags.*, SymUtils.*, NameKinds.*
88
import ast.*
9+
import Names.Name
910
import Phases.Phase
1011
import DenotTransformers.{DenotTransformer, IdentityDenotTransformer, SymTransformer}
1112
import NamerOps.{methodType, linkConstructorParams}
@@ -125,17 +126,19 @@ abstract class Recheck extends Phase, SymTransformer:
125126
tree.tpe
126127

127128
/** Keep the symbol of the `select` but re-infer its type */
128-
def recheckSelect(tree: Select)(using Context): Type = tree match
129-
case Select(qual, name) =>
130-
val qualType = recheck(qual).widenIfUnstable
131-
if name.is(OuterSelectName) then tree.tpe
132-
else
133-
//val pre = ta.maybeSkolemizePrefix(qualType, name)
134-
val mbr = qualType.findMember(name, qualType,
135-
excluded = if tree.symbol.is(Private) then EmptyFlags else Private
136-
).suchThat(tree.symbol == _)
137-
constFold(tree, qualType.select(name, mbr))
138-
//.showing(i"recheck select $qualType . $name : ${mbr.symbol.info} = $result")
129+
def recheckSelect(tree: Select)(using Context): Type =
130+
val Select(qual, name) = tree
131+
recheckSelection(tree, recheck(qual).widenIfUnstable, name)
132+
133+
def recheckSelection(tree: Select, qualType: Type, name: Name)(using Context) =
134+
if name.is(OuterSelectName) then tree.tpe
135+
else
136+
//val pre = ta.maybeSkolemizePrefix(qualType, name)
137+
val mbr = qualType.findMember(name, qualType,
138+
excluded = if tree.symbol.is(Private) then EmptyFlags else Private
139+
).suchThat(tree.symbol == _)
140+
constFold(tree, qualType.select(name, mbr))
141+
//.showing(i"recheck select $qualType . $name : ${mbr.symbol.info} = $result")
139142

140143
def recheckBind(tree: Bind, pt: Type)(using Context): Type = tree match
141144
case Bind(name, body) =>

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,18 @@ class CheckCaptures extends Recheck, SymTransformer:
238238
else i"references $cs1 are not all"
239239
report.error(i"$header included in allowed capture set ${res.blocking}", pos)
240240

241+
override def recheckSelection(tree: Select, qualType: Type, name: Name)(using Context) = {
242+
val selType = super.recheckSelection(tree, qualType, name)
243+
val selCs = selType.widen.captureSet
244+
if selCs.isAlwaysEmpty || selType.widen.isBoxedCapturing || qualType.isBoxedCapturing then
245+
selType
246+
else
247+
val qualCs = qualType.captureSet
248+
//println(i"intersect $qualType, ${selType.widen}, $qualCs, $selCs")
249+
if selCs.subCaptures(qualCs, frozen = true).isOK then selType
250+
else selType.widen.stripCapturing.capturing(selCs ** qualCs)
251+
}//.showing(i"recheck sel $tree, $qualType = $result")
252+
241253
override def recheckClosure(tree: Closure, pt: Type)(using Context): Type =
242254
val cs = capturedVars(tree.meth.symbol)
243255
capt.println(i"typing closure $tree with cvs $cs")

tests/neg-custom-args/captures/caseclass/Test_2.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,4 @@ def test(c: C) =
2222

2323
val y4 = y3 match
2424
case Ref(xx) => xx
25-
val y4c: {x3} () -> Unit = y4 // error (?) found: (y4 : {*} () -> Unit) required: {x3} () -> Unit. (But in fact it should work)
26-
27-
28-
29-
30-
25+
val y4c: {x3} () -> Unit = y4
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class CC
2+
type Cap = {*} CC
3+
4+
def test(cap1: Cap, cap2: Cap) =
5+
var b: List[String => String] = Nil // was error, now OK
6+
val bc = b.head // error

tests/neg-custom-args/captures/i15116.check

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@
99
5 | val x = Foo(m) // error
1010
| ^^^^^^^^^^^^^^
1111
| Non-local value x cannot have an inferred type
12-
| {Baz.this.m} Foo{m: {Baz.this.m} String}
13-
| with non-empty capture set {Baz.this.m}.
12+
| {Baz.this} Foo{m: {Baz.this} String}
13+
| with non-empty capture set {Baz.this}.
14+
| The type needs to be declared explicitly.
15+
-- Error: tests/neg-custom-args/captures/i15116.scala:7:6 --------------------------------------------------------------
16+
7 | val x = Foo(m) // error
17+
| ^^^^^^^^^^^^^^
18+
| Non-local value x cannot have an inferred type
19+
| {Bar1.this.m} Foo{m: {Bar1.this.m} String}
20+
| with non-empty capture set {Bar1.this.m}.
21+
| The type needs to be declared explicitly.
22+
-- Error: tests/neg-custom-args/captures/i15116.scala:9:6 --------------------------------------------------------------
23+
9 | val x = Foo(m) // error
24+
| ^^^^^^^^^^^^^^
25+
| Non-local value x cannot have an inferred type
26+
| {Baz2.this} Foo{m: {Baz2.this} String}
27+
| with non-empty capture set {Baz2.this}.
1428
| The type needs to be declared explicitly.

tests/neg-custom-args/captures/i15116.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ class Bar(val m: {*} String):
33
val x = Foo(m) // error
44
trait Baz(val m: {*} String):
55
val x = Foo(m) // error
6+
class Bar1(m: {*} String):
7+
val x = Foo(m) // error
8+
trait Baz2(m: {*} String):
9+
val x = Foo(m) // error
Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
1-
case class Ref(x: {*} String)
2-
31
@annotation.capability class C
4-
def test(c: C) =
5-
val x1 = Ref("hello")
6-
val y = x1 match
7-
case Ref(z) => z
2+
object test1:
3+
case class Ref(x: {*} String)
4+
5+
def test(c: C) =
6+
val x1 = Ref("hello")
7+
val y = x1 match
8+
case Ref(z) => z
9+
val yc: String = y
10+
11+
object test2:
12+
case class Ref(x: () => Unit)
13+
def test(c: C) =
14+
15+
val pure: () -> Unit = () => ()
16+
val impure: () => Unit = pure
17+
val mixed: {c} () -> Unit = pure
18+
val x = Ref(impure)
19+
val y0 = x.copy(pure)
20+
val yc0: Ref = y0
21+
22+
val x2 = Ref(pure)
23+
val _: Ref = x2
24+
val y2 = x2.copy()
25+
val yc2: Ref = y2
26+
27+
val x3 = Ref(mixed)
28+
val _: {c} Ref = x3
29+
val y3 = x3.copy()
30+
val yc3: {c} Ref = y3
31+
32+
val y4 = y3 match
33+
case Ref(xx) => xx
34+
val y4c: {x3} () -> Unit = y4

0 commit comments

Comments
 (0)