From c2bffc78b4d5a6e193cda648d98efc96034ac7f0 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 9 Nov 2023 11:18:14 +0100 Subject: [PATCH 1/3] Fix capture set variable installation in Setup --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 4 ++-- .../captures/cc-setup-impure-classes.scala | 5 +++++ .../captures/future-traverse.scala | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/pos-custom-args/captures/cc-setup-impure-classes.scala create mode 100644 tests/pos-custom-args/captures/future-traverse.scala diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 270fd9322a88..85942950f317 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -552,7 +552,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case tp: (TypeRef | AppliedType) => val sym = tp.typeSymbol if sym.isClass then - sym == defn.AnyClass + sym == defn.AnyClass || !sym.isPureClass // we assume Any is a shorthand of {cap} Any, so if Any is an upper // bound, the type is taken to be impure. else @@ -708,4 +708,4 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def postCheck()(using Context): Unit = for chk <- todoAtPostCheck do chk(ctx) todoAtPostCheck.clear() -end Setup \ No newline at end of file +end Setup diff --git a/tests/pos-custom-args/captures/cc-setup-impure-classes.scala b/tests/pos-custom-args/captures/cc-setup-impure-classes.scala new file mode 100644 index 000000000000..db88851b6a52 --- /dev/null +++ b/tests/pos-custom-args/captures/cc-setup-impure-classes.scala @@ -0,0 +1,5 @@ +import language.experimental.captureChecking + +trait Resource +def id[X](x: X): x.type = x +def foo[M <: Resource](r: M^): Unit = id(r) diff --git a/tests/pos-custom-args/captures/future-traverse.scala b/tests/pos-custom-args/captures/future-traverse.scala new file mode 100644 index 000000000000..5aedc5d29852 --- /dev/null +++ b/tests/pos-custom-args/captures/future-traverse.scala @@ -0,0 +1,16 @@ +import language.experimental.captureChecking + +trait Builder[-A, +C] +trait BuildFrom[-From, -A, +C] { + def newBuilder(from: From): Builder[A, C] +} + +trait Future[+T] { this: Future[T]^ => + import Future.* + def foldLeft[R](r: R): R = r + def traverse[A, B, M[X] <: IterableOnce[X]](in: M[A]^, bf: BuildFrom[M[A]^, B, M[B]^]): Unit = + foldLeft(successful(bf.newBuilder(in))) +} +object Future { + def successful[T](result: T): Future[T] = ??? +} From a1d705993a8f6416581a1bd69db091f237396c47 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 9 Nov 2023 13:55:31 +0100 Subject: [PATCH 2/3] Polishments --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 18 ++++++++++-------- .../captures/cc-setup-impure-classes.scala | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 85942950f317..d29a00a52f56 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -543,7 +543,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: end postProcess end setupTraverser - private def superTypeIsImpure(tp: Type)(using Context): Boolean = { + /** Checks whether an abstract type could be impure. See also: [[needsVariable]]. */ + private def instanceCanBeImpure(tp: Type)(using Context): Boolean = { tp.dealiasKeepAnnots match case CapturingType(_, refs) => !refs.isAlwaysEmpty @@ -552,20 +553,21 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case tp: (TypeRef | AppliedType) => val sym = tp.typeSymbol if sym.isClass then - sym == defn.AnyClass || !sym.isPureClass + sym == defn.AnyClass // we assume Any is a shorthand of {cap} Any, so if Any is an upper // bound, the type is taken to be impure. + || !sym.isPureClass else - sym != defn.Caps_Cap && superTypeIsImpure(tp.superType) + sym != defn.Caps_Cap && instanceCanBeImpure(tp.superType) case tp: (RefinedOrRecType | MatchType) => - superTypeIsImpure(tp.underlying) + instanceCanBeImpure(tp.underlying) case tp: AndType => - superTypeIsImpure(tp.tp1) || superTypeIsImpure(tp.tp2) + instanceCanBeImpure(tp.tp1) || instanceCanBeImpure(tp.tp2) case tp: OrType => - superTypeIsImpure(tp.tp1) && superTypeIsImpure(tp.tp2) + instanceCanBeImpure(tp.tp1) && instanceCanBeImpure(tp.tp2) case _ => false - }.showing(i"super type is impure $tp = $result", capt) + }.showing(i"instance can be impure $tp = $result", capt) /** Should a capture set variable be added on type `tp`? */ def needsVariable(tp: Type)(using Context): Boolean = { @@ -577,7 +579,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: else val tp1 = tp.dealiasKeepAnnots if tp1 ne tp then needsVariable(tp1) - else superTypeIsImpure(tp1) + else instanceCanBeImpure(tp1) case tp: (RefinedOrRecType | MatchType) => needsVariable(tp.underlying) case tp: AndType => diff --git a/tests/pos-custom-args/captures/cc-setup-impure-classes.scala b/tests/pos-custom-args/captures/cc-setup-impure-classes.scala index db88851b6a52..04dfb665b6d4 100644 --- a/tests/pos-custom-args/captures/cc-setup-impure-classes.scala +++ b/tests/pos-custom-args/captures/cc-setup-impure-classes.scala @@ -2,4 +2,5 @@ import language.experimental.captureChecking trait Resource def id[X](x: X): x.type = x -def foo[M <: Resource](r: M^): Unit = id(r) +def foo[M <: Resource](r: M^): Unit = id(r) // was error, should be ok +def bar[M](r: M^): Unit = id(r) // ok From 7307f34cd084a87e9209b1dd0a56deb072d92ae4 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Fri, 10 Nov 2023 17:34:46 +0100 Subject: [PATCH 3/3] Drop redundant check for AnyClass --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index d29a00a52f56..1620e54c0530 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -553,10 +553,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case tp: (TypeRef | AppliedType) => val sym = tp.typeSymbol if sym.isClass then - sym == defn.AnyClass - // we assume Any is a shorthand of {cap} Any, so if Any is an upper - // bound, the type is taken to be impure. - || !sym.isPureClass + !sym.isPureClass else sym != defn.Caps_Cap && instanceCanBeImpure(tp.superType) case tp: (RefinedOrRecType | MatchType) =>