Skip to content

Non-copyable generics fixes, part 10 #72164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2496,7 +2496,7 @@ ERROR(inferred_opaque_type,none,

// Inverse Constraints
ERROR(inverse_type_not_invertible,none,
"type %0 is not invertible", (Type))
"type %0 cannot be suppressed", (Type))

// Extensions
ERROR(non_nominal_extension,none,
Expand Down Expand Up @@ -3168,12 +3168,11 @@ ERROR(requires_not_suitable_archetype,none,
(Type))

ERROR(requires_not_suitable_inverse_subject,none,
"cannot apply inverse '~%1' to type %0 in conformance requirement",
"cannot suppress '~%1' on type %0",
(Type, StringRef))

ERROR(requires_not_suitable_inverse_outer_subject,none,
"cannot add inverse constraint '%0: ~%1' "
"on generic parameter '%0' defined in outer scope",
"cannot suppress '~%1' on generic parameter '%0' defined in outer scope",
(StringRef, StringRef))

ERROR(invalid_shape_requirement,none,
Expand Down Expand Up @@ -7619,7 +7618,7 @@ ERROR(inverse_conflicts_explicit_composition, none,
"composition cannot contain '~%0' when another member requires '%0'",
(StringRef))
ERROR(inverse_extension, none,
"cannot apply inverse %0 to extension",
"cannot suppress %0 in extension",
(Type))
ERROR(copyable_illegal_deinit, none,
"deinitializer cannot be declared in %kind0 that conforms to 'Copyable'",
Expand Down
7 changes: 4 additions & 3 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3060,9 +3060,10 @@ directReferencesForTypeRepr(Evaluator &evaluator,
inverseRepr->getConstraint(), dc,
allowUsableFromInline);
if (innerResult.first.size() == 1) {
auto *proto = dyn_cast<ProtocolDecl>(innerResult.first[0]);
if (auto ip = proto->getInvertibleProtocolKind()) {
result.second.insert(*ip);
if (auto *proto = dyn_cast<ProtocolDecl>(innerResult.first[0])) {
if (auto ip = proto->getInvertibleProtocolKind()) {
result.second.insert(*ip);
}
}
}

Expand Down
15 changes: 5 additions & 10 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
#include "swift/AST/ForeignErrorConvention.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/InverseMarking.h"
#include "swift/AST/MacroDefinition.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
Expand Down Expand Up @@ -3218,21 +3217,17 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
static void diagnoseInverseOnClass(ClassDecl *decl) {
auto &ctx = decl->getASTContext();

for (auto ip : InvertibleProtocolSet::full()) {
auto inverseMarking = decl->hasInverseMarking(ip);

// Inferred inverses are already ignored for classes.
// FIXME: we can also diagnose @_moveOnly here if we use `isAnyExplicit`
if (!inverseMarking.is(InverseMarking::Kind::Explicit))
continue;
InvertibleProtocolSet inverses;
bool anyObject = false;
(void) getDirectlyInheritedNominalTypeDecls(decl, inverses, anyObject);

for (auto ip : inverses) {
// Allow ~Copyable when MoveOnlyClasses is enabled
if (ip == InvertibleProtocolKind::Copyable
&& ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses))
continue;


ctx.Diags.diagnose(inverseMarking.getLoc(),
ctx.Diags.diagnose(decl->getLoc(),
diag::inverse_on_class,
getProtocolName(getKnownProtocolKind(ip)));
}
Expand Down
10 changes: 9 additions & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2424,8 +2424,16 @@ checkIndividualConformance(NormalProtocolConformance *conformance) {
implyingConf = implyingConf->getImplyingConformance();
}

// If the conditional requirements all have the form `T : Copyable`, then
// we accept the implied conformance with the same conditional requirements.
auto implyingCondReqs = implyingConf->getConditionalRequirements();
if (!implyingCondReqs.empty()) {
bool allCondReqsInvertible = llvm::all_of(implyingCondReqs,
[&](Requirement req) {
return (req.getKind() == RequirementKind::Conformance &&
req.getProtocolDecl()->getInvertibleProtocolKind());
});

if (!allCondReqsInvertible) {
// FIXME:
// We shouldn't suggest including witnesses for the conformance, because
// those suggestions will go in the current DeclContext, but really they
Expand Down
2 changes: 1 addition & 1 deletion test/Generics/inverse_extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protocol HasAssoc {
associatedtype A
}
extension HasAssoc where Self.A: ~Copyable {}
// expected-error@-1 {{cannot add inverse constraint 'Self.A: ~Copyable' on generic parameter 'Self.A' defined in outer scope}}
// 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'}}

class Box<T: ~Copyable> {}
Expand Down
4 changes: 2 additions & 2 deletions test/Generics/inverse_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ class NiceTry: ~Copyable, Copyable {} // expected-error {{classes cannot be '~Co


struct Extendo: ~Copyable {}
extension Extendo: Copyable, ~Copyable {} // expected-error {{cannot apply inverse '~Copyable' to extension}}
extension Extendo: Copyable, ~Copyable {} // expected-error {{cannot suppress '~Copyable' in extension}}
// expected-error@-1 {{struct 'Extendo' required to be 'Copyable' but is marked with '~Copyable'}}

enum EnumExtendo {}
extension EnumExtendo: ~Copyable {} // expected-error {{cannot apply inverse '~Copyable' to extension}}
extension EnumExtendo: ~Copyable {} // expected-error {{cannot suppress '~Copyable' in extension}}

extension NeedsCopyable where Self: ~Copyable {}
// expected-error@-1 {{'Self' required to be 'Copyable' but is marked with '~Copyable'}}
Expand Down
20 changes: 10 additions & 10 deletions test/Generics/inverse_scoping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
protocol NoCopyReq: ~Copyable {}

protocol P {
func f() where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
func f() where Self: ~Copyable // expected-error {{cannot suppress '~Copyable' on generic parameter 'Self' defined in outer scope}}

func g<T>(_: T) where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
func g<T>(_: T) where Self: ~Copyable // expected-error {{cannot suppress '~Copyable' on generic parameter 'Self' defined in outer scope}}

associatedtype AT where Self: ~Copyable // expected-error {{constraint with subject type of 'Self' is not supported; consider adding requirement to protocol inheritance clause instead}}

// expected-error@+1 {{cannot add inverse constraint 'Self.Alice: ~Copyable' on generic parameter 'Self.Alice' defined in outer scope}}
// expected-error@+1 {{cannot suppress '~Copyable' on generic parameter 'Self.Alice' defined in outer scope}}
associatedtype Bob where Alice: NoCopyReq & ~Copyable
associatedtype Alice where Bob: ~Copyable
// expected-error@-1 {{cannot add inverse constraint 'Self.Bob: ~Copyable' on generic parameter 'Self.Bob' defined in outer scope}}
// expected-error@-1 {{cannot suppress '~Copyable' on generic parameter 'Self.Bob' defined in outer scope}}
}

protocol U {}
Expand All @@ -23,13 +23,13 @@ extension U where Self: ~Copyable {}
// expected-error@-1 {{'Self' required to be 'Copyable' but is marked with '~Copyable'}}

extension P where Self: ~Copyable {
func g() where Self: ~Copyable, // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
func g() where Self: ~Copyable, // expected-error {{cannot suppress '~Copyable' on generic parameter 'Self' defined in outer scope}}
// FIXME: why no similar 2nd error as Escapable here on Self?

Self: ~Escapable {} // expected-error {{cannot add inverse constraint 'Self: ~Escapable' on generic parameter 'Self' defined in outer scope}}
Self: ~Escapable {} // expected-error {{cannot suppress '~Escapable' on generic parameter 'Self' defined in outer scope}}
// expected-error@-1 {{'Self' required to be 'Escapable' but is marked with '~Escapable'}}

typealias Me = Self where Self: ~Copyable // expected-error {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
typealias Me = Self where Self: ~Copyable // expected-error {{cannot suppress '~Copyable' on generic parameter 'Self' defined in outer scope}}

typealias MeAndU = Self where Self: U
}
Expand All @@ -39,15 +39,15 @@ struct S<T> {
// expected-note@+2 3{{add}}
// expected-error@+1 {{parameter of noncopyable type 'U' must specify ownership}}
func fn<U>(_ u: U)
where T: ~Copyable, // expected-error {{cannot add inverse constraint 'T: ~Copyable' on generic parameter 'T' defined in outer scope}}
where T: ~Copyable, // expected-error {{cannot suppress '~Copyable' on generic parameter 'T' defined in outer scope}}
// expected-error@-1 {{'T' required to be 'Copyable' but is marked with '~Copyable'}}
U: ~Copyable
{}

func onlyCopyable() where T: Copyable {}

func fn<U>(_ u: U)
where T: ~Escapable, // expected-error {{cannot add inverse constraint 'T: ~Escapable' on generic parameter 'T' defined in outer scope}}
where T: ~Escapable, // expected-error {{cannot suppress '~Escapable' on generic parameter 'T' defined in outer scope}}
// expected-error@-1 {{'T' required to be 'Escapable' but is marked with '~Escapable'}}
U: ~Escapable
{}
Expand All @@ -57,5 +57,5 @@ extension S where T: NoCopyReq & ~Copyable {}
// expected-error@-1 {{'T' required to be 'Copyable' but is marked with '~Copyable'}}

struct ExtraInverse<T: ~Copyable> {
func check() where T: ~Copyable {} // expected-error {{cannot add inverse constraint 'T: ~Copyable' on generic parameter 'T' defined in outer scope}}
func check() where T: ~Copyable {} // expected-error {{cannot suppress '~Copyable' on generic parameter 'T' defined in outer scope}}
}
46 changes: 25 additions & 21 deletions test/Parse/inverses.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ enum Maybe<Thing: ~Copyable> : ~Copyable {}
func more() {
let _: any ~Copyable = 19

let _: any ~Equatable = 19 // expected-error@:14 {{type 'Equatable' is not invertible}}
let _: any ~Equatable = 19 // expected-error@:14 {{type 'Equatable' cannot be suppressed}}

let _: any (~Copyable & ~Equatable) // expected-error{{type 'Equatable' is not invertible}}
let _: any (~Copyable & ~Equatable) // expected-error{{type 'Equatable' cannot be suppressed}}

let _: ~Any // expected-error {{type 'Any' is not invertible}}
let _: ~AnyObject // expected-error {{type 'AnyObject' is not invertible}}
let _: ~Any // expected-error {{type 'Any' cannot be suppressed}}
let _: ~AnyObject // expected-error {{type 'AnyObject' cannot be suppressed}}
}

struct S4: ~(Copyable & Equatable) {} // expected-error {{type 'Equatable' is not invertible}}
struct S4: ~(Copyable & Equatable) {} // expected-error {{type 'Equatable' cannot be suppressed}}

func blah<T>(_ t: borrowing T) where T: ~Copyable,
T: ~Hashable {} // expected-error@:41 {{type 'Hashable' is not invertible}}
T: ~Hashable {} // expected-error@:41 {{type 'Hashable' cannot be suppressed}}

func foo<T: ~Copyable>(x: borrowing T) {}

Expand All @@ -35,13 +35,13 @@ protocol Foo: ~Copyable
associatedtype Touch : ~Copyable,
~Copyable

func test<T>(_ t: T) where T: ~Self // expected-error {{type 'Self' is not invertible}}
func test<T>(_ t: T) where T: ~Self // expected-error {{type 'Self' cannot be suppressed}}
}

protocol Sando { func make() }

class C: ~Copyable, // expected-error {{classes cannot be '~Copyable'}}
~Sando // expected-error {{type 'Sando' is not invertible}}
~Sando // expected-error {{type 'Sando' cannot be suppressed}}
{}

public struct MoveOnlyS1<T> : ~Copyable { /*deinit {}*/ }
Expand All @@ -52,12 +52,12 @@ protocol Rope<Element>: Hashable, ~Copyable { // expected-error {{'Self' requir
associatedtype Element: ~Copyable
}

extension S: ~Copyable {} // expected-error {{cannot apply inverse '~Copyable' to extension}}
extension S: ~Copyable {} // expected-error {{cannot suppress '~Copyable' in extension}}

struct S: ~U, // expected-error {{type 'U' is not invertible}}
struct S: ~U, // expected-error {{type 'U' cannot be suppressed}}
~Copyable {}

func greenBay<each T: ~Copyable>(_ r: repeat each T) {} // expected-error{{cannot apply inverse '~Copyable' to type 'each T' in conformance requirement}}
func greenBay<each T: ~Copyable>(_ r: repeat each T) {} // expected-error{{cannot suppress '~Copyable' on type 'each T'}}

typealias Clone = Copyable
func dup<D: ~Clone>(_ d: D) {}
Expand All @@ -73,28 +73,32 @@ func superb(_ thing: some ~Copyable, thing2: some ~Clone) {}
// expected-note@-2 2{{add 'inout'}}
// expected-note@-3 2{{add 'consuming'}}

func ownership1(_ t: borrowing any ~Equatable) {} // expected-error {{type 'Equatable' is not invertible}}
func ownership1(_ t: borrowing any ~Equatable) {} // expected-error {{type 'Equatable' cannot be suppressed}}

func ownership2(_ t: ~ borrowing Int) {} // expected-error {{cannot find type 'borrowing' in scope}}
// expected-error@-1 {{unnamed parameters must be written with the empty name '_'}}
// expected-error@-2 {{expected ',' separator}}

func ownership3(_ t: consuming some ~Clone) {}

func what(one: ~Copyable..., // expected-error {{type 'any Copyable' is not invertible}}
func what(one: ~Copyable..., // expected-error {{type 'any Copyable' cannot be suppressed}}
two: ~(Copyable...) // expected-error {{variadic parameter cannot appear outside of a function parameter list}}
// expected-error@-1 {{type 'any Copyable' is not invertible}}
// expected-error@-1 {{type 'any Copyable' cannot be suppressed}}
) {}

struct A { struct B { struct C {} } }

typealias Z1 = (~Copyable).Type // FIXME: should be an error
typealias Z1 = ~Copyable.Type // expected-error {{type 'any Copyable.Type' is not invertible}}
typealias Z2 = ~A.B.C // expected-error {{type 'A.B.C' is not invertible}}
typealias Z3 = ~A? // expected-error {{type 'A?' is not invertible}}
typealias Z4 = ~Rope<Int> // expected-error {{type 'Rope<Int>' is not invertible}}
typealias Z5 = (~Int) -> Void // expected-error {{type 'Int' is not invertible}}
typealias Z1 = ~Copyable.Type // expected-error {{type 'any Copyable.Type' cannot be suppressed}}
typealias Z2 = ~A.B.C // expected-error {{type 'A.B.C' cannot be suppressed}}
typealias Z3 = ~A? // expected-error {{type 'A?' cannot be suppressed}}
typealias Z4 = ~Rope<Int> // expected-error {{type 'Rope<Int>' cannot be suppressed}}
typealias Z5 = (~Int) -> Void // expected-error {{type 'Int' cannot be suppressed}}
typealias Z6 = ~() -> () // expected-error {{single argument function types require parentheses}}
// expected-error@-1 {{type '()' is not invertible}}
typealias Z7 = ~(Copyable & Hashable) // expected-error {{type 'Hashable' is not invertible}}
// expected-error@-1 {{type '()' cannot be suppressed}}
typealias Z7 = ~(Copyable & Hashable) // expected-error {{type 'Hashable' cannot be suppressed}}
typealias Z8 = ~Copyable & Hashable // expected-error {{composition cannot contain '~Copyable' when another member requires 'Copyable'}}

struct NotAProtocol {}

struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
22 changes: 22 additions & 0 deletions test/Sema/conditionally_copyable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics

struct G<T: ~Copyable>: ~Copyable {}

extension G: Copyable {}

protocol Base {}
protocol Derived: Base {}

// Normally we would require the conditional conformance 'G: Base' to be
// explicitly declared, but that would break source compatibility if G
// used to be unconditionally Copyable.

extension G: Derived {} // expected-note {{requirement from conditional conformance of 'G<NotCopyable>' to 'Base'}}

struct NotCopyable: ~Copyable {}
struct IsCopyable {}

func f<T: Base>(_: T.Type) {}

f(G<NotCopyable>.self) // expected-error {{global function 'f' requires that 'NotCopyable' conform to 'Copyable'}}
f(G<IsCopyable>.self) // accepted