From 64abbed84e2cb41a2b684ebf13a229cd9fa42990 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 15 Dec 2025 16:46:20 -0800 Subject: [PATCH 1/2] Sema: add coverage for existential suppression --- test/Generics/inverse_assoc_types.swift | 26 ++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/Generics/inverse_assoc_types.swift b/test/Generics/inverse_assoc_types.swift index 8ed117b33ba75..24b39d128a62f 100644 --- a/test/Generics/inverse_assoc_types.swift +++ b/test/Generics/inverse_assoc_types.swift @@ -53,7 +53,7 @@ protocol Iterable: ~Copyable { struct ReqC {} -func reqC(_ t: T) {} // expected-note 8{{'where T: Copyable' is implicit here}} +func reqC(_ t: T) {} // expected-note 14{{'where T: Copyable' is implicit here}} protocol ProvideA: ~Copyable { @@ -130,9 +130,29 @@ func checkExistential(_ s: any Gen) { reqC(s.i().e().i().e()) } +func checkExistential2(_ s: any Gen) { + reqC(s.e()) // expected-error {{global function 'reqC' requires that 'R' conform to 'Copyable'}} + reqC(s.e().e()) // expected-error {{value of type 'R' has no member 'e'}} + + reqC(s.i()) // expected-error {{requires that 'T' conform to 'Copyable'}} + reqC(s.i().e()) + reqC(s.i().e().i()) // expected-error {{requires that 'T' conform to 'Copyable'}} + reqC(s.i().e().i().e()) +} + +// Suppression on the constrained existential only goes far down +// as possible to suppress on the generic type parameter itself. +func checkExistential3(_ s: any Gen) where R: Gen, R: ~Copyable, R.E: ~Copyable { + reqC(s.e()) // expected-error {{global function 'reqC' requires that 'R' conform to 'Copyable'}} + reqC(s.e().e()) // expected-error {{global function 'reqC' requires that 'R.E' conform to 'Copyable'}} + reqC(s.e().e().e()) +} + protocol Veggie { associatedtype A: ~Copyable associatedtype NeedsCopyable + + func a() -> A } protocol Carrot: Veggie where Self.NeedsCopyable: ~Copyable {} // expected-error {{'Self.NeedsCopyable' required to be 'Copyable' but is marked with '~Copyable'}} @@ -141,6 +161,10 @@ protocol Carrot: Veggie protocol CarrotCake: Carrot where Self.A: ~Copyable {} // expected-error {{'Self.A' required to be 'Copyable' but is marked with '~Copyable'}} // expected-error @-1{{cannot suppress '~Copyable' on generic parameter 'Self.A' defined in outer scope}} +func ex1(_ nc: any Veggie, c: any Veggie) { + reqC(nc.a()) // expected-error {{global function 'reqC' requires that 'Cucumber' conform to 'Copyable'}} + reqC(c.a()) +} protocol Bird { associatedtype Song From fbd51c06df5fb3e2f73f47b3778901c31de250dc Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 15 Dec 2025 16:48:50 -0800 Subject: [PATCH 2/2] Sema: apply scoping SuppAssocType scoping rule roots The generic parameter at the root of a DependentMemberType is the main thing we care about in terms of the scoping rule. We have a check for ineffective / invalid inverses in a later phase, so if the suppression on the type member was wrong then we should still raise an error about it. --- lib/AST/RequirementMachine/ApplyInverses.cpp | 3 ++- test/Generics/inverse_assoc_types.swift | 4 ---- test/Generics/inverse_extensions.swift | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/AST/RequirementMachine/ApplyInverses.cpp b/lib/AST/RequirementMachine/ApplyInverses.cpp index 7467c86e66c46..e86524018de5e 100644 --- a/lib/AST/RequirementMachine/ApplyInverses.cpp +++ b/lib/AST/RequirementMachine/ApplyInverses.cpp @@ -204,7 +204,8 @@ void swift::rewriting::applyInverses( // func f() where Self: ~Copyable // } // - if (representativeGPs.find(canSubject) == representativeGPs.end()) { + auto subjectRoot = canSubject->getDependentMemberRoot()->getCanonicalType(); + if (representativeGPs.find(subjectRoot) == representativeGPs.end()) { errors.push_back( RequirementError::forInvalidInverseOuterSubject(inverse)); continue; diff --git a/test/Generics/inverse_assoc_types.swift b/test/Generics/inverse_assoc_types.swift index 24b39d128a62f..35ead9097ad8b 100644 --- a/test/Generics/inverse_assoc_types.swift +++ b/test/Generics/inverse_assoc_types.swift @@ -156,10 +156,8 @@ protocol Veggie { } protocol Carrot: Veggie where Self.NeedsCopyable: ~Copyable {} // expected-error {{'Self.NeedsCopyable' required to be 'Copyable' but is marked with '~Copyable'}} - // expected-error @-1{{cannot suppress '~Copyable' on generic parameter 'Self.NeedsCopyable' defined in outer scope}} protocol CarrotCake: Carrot where Self.A: ~Copyable {} // expected-error {{'Self.A' required to be 'Copyable' but is marked with '~Copyable'}} -// expected-error @-1{{cannot suppress '~Copyable' on generic parameter 'Self.A' defined in outer scope}} func ex1(_ nc: any Veggie, c: any Veggie) { reqC(nc.a()) // expected-error {{global function 'reqC' requires that 'Cucumber' conform to 'Copyable'}} @@ -171,8 +169,6 @@ protocol Bird { } protocol Eagle: Bird where Self.Song: ~Copyable {}// expected-error {{'Self.Song' required to be 'Copyable' but is marked with '~Copyable'}} -// expected-error @-1{{cannot suppress '~Copyable' on generic parameter 'Self.Song' defined in outer scope}} - protocol Pushable { associatedtype Element: ~Copyable diff --git a/test/Generics/inverse_extensions.swift b/test/Generics/inverse_extensions.swift index 9e0514b65f248..686ed2ca05a18 100644 --- a/test/Generics/inverse_extensions.swift +++ b/test/Generics/inverse_extensions.swift @@ -16,8 +16,7 @@ protocol HasAssoc { associatedtype A } extension HasAssoc where Self.A: ~Copyable {} -// expected-error@-1 {{cannot suppress '~Copyable' on generic parameter 'Self.A' defined in outer scope}} -// expected-error@-2 {{'Self.A' required to be 'Copyable' but is marked with '~Copyable'}} +// expected-error@-1 {{'Self.A' required to be 'Copyable' but is marked with '~Copyable'}} class Box {} extension Box where T: ~Copyable {}