Skip to content

[6.0🍒] NCGenerics: introduce SuppressedAssociatedTypes #72812

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 2 commits into from
Apr 4, 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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7618,6 +7618,9 @@ ERROR(inverse_but_also_conforms, none,
ERROR(inverse_generic_but_also_conforms, none,
"%0 required to be '%1' but is marked with '~%1'",
(Type, StringRef))
ERROR(inverse_associatedtype_restriction, none,
"cannot suppress '%0' requirement of an associated type",
(StringRef))
ERROR(inverse_on_class, none,
"classes cannot be '~%0'",
(StringRef))
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NoncopyableGenerics, true)
// Alias for NoncopyableGenerics
EXPERIMENTAL_FEATURE(NoncopyableGenerics2, true)

// Enables ~Copyable and ~Escapable annotations on associatedtype declarations.
EXPERIMENTAL_FEATURE(SuppressedAssociatedTypes, true)

/// Allow destructuring stored `let` bindings in structs.
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)

Expand Down
1 change: 1 addition & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ static bool usesFeatureRawLayout(Decl *decl) {
}

UNINTERESTING_FEATURE(Embedded)
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)

static bool usesFeatureNoncopyableGenerics(Decl *decl) {
if (decl->getAttrs().hasAttribute<PreInverseGenericsAttr>())
Expand Down
30 changes: 13 additions & 17 deletions lib/AST/RequirementMachine/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,22 @@ bool swift::rewriting::diagnoseRequirementErrors(
}

case RequirementError::Kind::InvalidInverseSubject: {
auto requirement = error.getRequirement();
if (requirement.hasError())
break;

assert(requirement.getKind() == RequirementKind::Conformance);

auto subjectType = requirement.getFirstType();
auto constraintType = requirement.getSecondType();

assert(constraintType->is<ProtocolCompositionType>());
auto inverse = error.getInverse();
auto subjectType = inverse.subject;
auto protoKind = getKnownProtocolKind(inverse.getKind());

// Pick one of the inverses to diagnose.
auto inverses =
constraintType->getAs<ProtocolCompositionType>()->getInverses();
assert(!inverses.empty());
StringRef name = getProtocolName(protoKind);

StringRef name = getProtocolName(getKnownProtocolKind(*inverses.begin()));
if (subjectType->is<DependentMemberType>()) {
// explain that associated types can't have inverses
ctx.Diags.diagnose(loc, diag::inverse_associatedtype_restriction,
name);
} else {
// generic diagnostic
ctx.Diags.diagnose(loc, diag::requires_not_suitable_inverse_subject,
subjectType, name);
}

ctx.Diags.diagnose(loc, diag::requires_not_suitable_inverse_subject,
subjectType, name);
diagnosedError = true;
break;
}
Expand Down
15 changes: 8 additions & 7 deletions lib/AST/RequirementMachine/Diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,15 @@ struct RequirementError {

public:
Requirement getRequirement() const {
assert(!(kind == Kind::InvalidInverseOuterSubject ||
kind == Kind::ConflictingInverseRequirement));
assert(kind != Kind::InvalidInverseOuterSubject &&
kind != Kind::InvalidInverseSubject &&
kind != Kind::ConflictingInverseRequirement);
return requirement;
}

InverseRequirement getInverse() const {
assert(kind == Kind::InvalidInverseOuterSubject ||
kind == Kind::InvalidInverseSubject ||
kind == Kind::ConflictingInverseRequirement);
return inverse;
}
Expand All @@ -107,14 +109,13 @@ struct RequirementError {
return {Kind::InvalidRequirementSubject, req, loc};
}

static RequirementError forInvalidInverseSubject(Requirement req,
SourceLoc loc) {
return {Kind::InvalidInverseSubject, req, loc};
static RequirementError forInvalidInverseSubject(InverseRequirement inv) {
return {Kind::InvalidInverseSubject, inv, inv.loc};
}

static
RequirementError forInvalidInverseOuterSubject(InverseRequirement req) {
return {Kind::InvalidInverseOuterSubject, req, req.loc};
RequirementError forInvalidInverseOuterSubject(InverseRequirement inv) {
return {Kind::InvalidInverseOuterSubject, inv, inv.loc};
}

static RequirementError forConflictingInverseRequirement(
Expand Down
26 changes: 21 additions & 5 deletions lib/AST/RequirementMachine/RequirementLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,8 @@ static void desugarConformanceRequirement(
// Only permit type-parameter subjects.
errors.push_back(
RequirementError::forInvalidRequirementSubject(req, loc));
} else if (subject->isParameterPack()) {
// Disallow parameter packs for now.
errors.push_back(RequirementError::forInvalidInverseSubject(req, loc));
} else {
// Record inverse.
// Record and desugar inverses.
auto &ctx = req.getFirstType()->getASTContext();
for (auto ip : compositionType->getInverses())
inverses.push_back({req.getFirstType(),
Expand Down Expand Up @@ -746,11 +743,30 @@ void swift::rewriting::applyInverses(
SmallVectorImpl<StructuralRequirement> &result,
SmallVectorImpl<RequirementError> &errors) {

// Summarize the inverses and flag ones that are incorrect.
// No inverses to even validate.
if (inverseList.empty())
return;

const bool allowInverseOnAssocType =
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes);

// Summarize the inverses and diagnose ones that are incorrect.
llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
for (auto inverse : inverseList) {
auto canSubject = inverse.subject->getCanonicalType();

// Inverses on associated types are experimental.
if (!allowInverseOnAssocType && canSubject->is<DependentMemberType>()) {
errors.push_back(RequirementError::forInvalidInverseSubject(inverse));
continue;
}

// Noncopyable checking support for parameter packs is not implemented yet.
if (canSubject->isParameterPack()) {
errors.push_back(RequirementError::forInvalidInverseSubject(inverse));
continue;
}

// WARNING: possible quadratic behavior, but should be OK in practice.
auto notInScope = llvm::none_of(gps, [=](Type t) {
return t->getCanonicalType() == canSubject;
Expand Down
1 change: 1 addition & 0 deletions stdlib/cmake/modules/SwiftSource.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ function(_compile_swift_files
endif()

list(APPEND swift_flags "-enable-experimental-feature" "NoncopyableGenerics2")
list(APPEND swift_flags "-enable-experimental-feature" "SuppressedAssociatedTypes")

if(SWIFT_ENABLE_EXPERIMENTAL_NONESCAPABLE_TYPES)
list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes")
Expand Down
42 changes: 42 additions & 0 deletions test/Generics/inverse_associatedtype_restriction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// RUN: %target-typecheck-verify-swift \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature NonescapableTypes

// The restriction is that we don't permit suppression requirements on
// associated types without an experimental feature for that.

protocol P {
associatedtype A: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
}

protocol P_Prime: P {}

protocol Primary<T> {
associatedtype T: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
}

// This is fine, since T isn't an associatedtype, it's substituted into one.
typealias AliasPrimary<T> = Primary<T> where T: ~Copyable

protocol S {
associatedtype One: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}

associatedtype Two: ~Escapable & ~Copyable
// expected-error@-1 {{cannot suppress 'Copyable' requirement of an associated type}}
// expected-error@-2 {{cannot suppress 'Escapable' requirement of an associated type}}

associatedtype Three: ~Escapable // expected-error {{cannot suppress 'Escapable' requirement of an associated type}}
}

protocol Base {
associatedtype A
}
protocol Derived: Base where Self.A: ~Copyable {} // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}

protocol Q {
associatedtype A where A: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
}

protocol R where Self.A: ~Copyable { // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
associatedtype A
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ func packIt() {
func packingUniqueHeat_1<each T: ~Copyable>(_ t: repeat each T) {}
// expected-error@-1{{cannot suppress '~Copyable' on type 'each T'}}
// expected-note@-2{{generic parameter 'each T' has an implicit Copyable requirement}}
// expected-error@-3{{'each T' required to be 'Copyable' but is marked with '~Copyable'}}

func packingUniqueHeat_2<each T>(_ t: repeat each T)
where repeat each T: ~Copyable {}
// expected-error@-1{{cannot suppress '~Copyable' on type 'each T'}}
// expected-note@-3{{generic parameter 'each T' has an implicit Copyable requirement}}
// expected-error@-3{{'each T' required to be 'Copyable' but is marked with '~Copyable'}}

func packItUniquely() {
packingUniqueHeat_1(MO())
Expand Down
3 changes: 2 additions & 1 deletion test/Generics/inverse_extensions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %target-typecheck-verify-swift \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature NonescapableTypes
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes

struct Turtle<T> {}
extension Turtle where T: ~Copyable {} // expected-error {{'T' required to be 'Copyable' but is marked with '~Copyable'}}
Expand Down
5 changes: 4 additions & 1 deletion test/Generics/inverse_generics.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes
// RUN: %target-typecheck-verify-swift \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes



Expand Down
2 changes: 1 addition & 1 deletion test/Generics/inverse_scoping.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes -enable-experimental-feature SuppressedAssociatedTypes



Expand Down
7 changes: 6 additions & 1 deletion test/Generics/inverse_signatures.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// RUN: %target-swift-frontend -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes -verify -typecheck %s -debug-generic-signatures -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:"
// RUN: %target-swift-frontend \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -verify -typecheck %s -debug-generic-signatures \
// RUN: -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:"

// CHECK-LABEL: (file).genericFn@
// CHECK: Generic signature: <T where T : Copyable, T : Escapable>
Expand Down
7 changes: 6 additions & 1 deletion test/IRGen/existential_shape_metadata_noncopyable.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -disable-availability-checking -enable-experimental-feature NoncopyableGenerics -module-name existential_shape_metadata | %IRGenFileCheck %s
// RUN: %target-swift-frontend \
// RUN: -emit-ir %s -swift-version 5 \
// RUN: -disable-availability-checking \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -module-name existential_shape_metadata | %IRGenFileCheck %s

// NOTE: Once noncopyable generics are enabled by default, merge this back into existential_shape_metadata.swift

Expand Down
1 change: 1 addition & 0 deletions test/IRGen/mangling_inverse_generics_evolution.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-ir -o - %s -module-name test \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -parse-as-library \
// RUN: -enable-library-evolution \
Expand Down
6 changes: 3 additions & 3 deletions test/Interpreter/moveonly_generics_associatedtype.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %target-swift-emit-sil %s -DBAD_COPY -verify -sil-verify-all -enable-experimental-feature NoncopyableGenerics
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics) | %FileCheck %s
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics) | %FileCheck %s
// RUN: %target-swift-emit-sil %s -DBAD_COPY -verify -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes) | %FileCheck %s
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes) | %FileCheck %s

// REQUIRES: executable_test
// REQUIRES: asserts
Expand Down
2 changes: 1 addition & 1 deletion test/ModuleInterface/invertible_constraints.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature NoncopyableGenerics
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test
// RUN: %FileCheck %s < %t.swiftinterface

Expand Down
2 changes: 1 addition & 1 deletion test/ModuleInterface/moveonly_interface_flag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -I %t
// RUN: %FileCheck %s < %t/Library.swiftinterface

// XFAIL: noncopyable_generics


// this test makes sure that decls containing a move-only type are guarded by the $MoveOnly feature flag

Expand Down
5 changes: 5 additions & 0 deletions test/ModuleInterface/noncopyable_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -o %t/NoncopyableGenerics_Misc.swiftmodule \
// RUN: -emit-module-interface-path %t/NoncopyableGenerics_Misc.swiftinterface \
// RUN: %S/Inputs/NoncopyableGenerics_Misc.swift

// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -enable-experimental-feature BorrowingSwitch \
// RUN: -o %t/Swiftskell.swiftmodule \
Expand All @@ -24,16 +26,19 @@

// RUN: %target-swift-frontend -compile-module-from-interface \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: %t/NoncopyableGenerics_Misc.swiftinterface -o %t/NoncopyableGenerics_Misc.swiftmodule

// RUN: %target-swift-frontend -compile-module-from-interface \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: %t/Swiftskell.swiftinterface -o %t/Swiftskell.swiftmodule

// RUN: %target-swift-frontend -emit-silgen -I %t %s \
// RUN: -enable-experimental-feature NoncopyableGenerics \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -o %t/final.silgen

Expand Down
4 changes: 3 additions & 1 deletion test/Parse/inverses.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes

protocol U {}

Expand Down Expand Up @@ -58,6 +58,7 @@ struct S: ~U, // expected-error {{type 'U' cannot be suppressed}}
~Copyable {}

func greenBay<each T: ~Copyable>(_ r: repeat each T) {} // expected-error{{cannot suppress '~Copyable' on type 'each T'}}
// expected-error@-1 {{'each T' required to be 'Copyable' but is marked with '~Copyable'}}

typealias Clone = Copyable
func dup<D: ~Clone>(_ d: D) {}
Expand Down Expand Up @@ -106,6 +107,7 @@ struct NotAProtocol {}

struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}


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

func typeInExpression() {
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/inverses_legacy.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %target-typecheck-verify-swift

// XFAIL: noncopyable_generics


protocol Sando { func make() } // expected-note {{protocol requires function 'make()'}}
// expected-note@-1 {{type 'U' does not conform to inherited protocol 'Copyable'}}
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/inverses_legacy_ifdef.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %swift-frontend -typecheck %s
// RUN: %swift-frontend -typecheck %s -verify -DHAVE_NCGENERICS

// XFAIL: noncopyable_generics


/// This test checks that you can write ~Copyable in places that were illegal
/// in Swift 5.9, as long as those illegal appearances are guarded within
Expand Down
3 changes: 2 additions & 1 deletion test/SIL/Parser/array_roundtrip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

// FIXME(NCG): This produces `cannot suppress conformances here` errors due to
// all the new <τ_0_0 where τ_0_0 : ~Copyable> clauses
// XFAIL: !noncopyable_generics
// rdar://124657305 (@substituted generic signatures need to either include inverses or the Copyable/Escapable conformances)
// XFAIL: *

var W = [UInt32](repeating: 0, count: 16)
1 change: 1 addition & 0 deletions test/SIL/lifetime_dependence_generics.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// RUN: %target-swift-frontend %s -emit-sil \
// RUN: -enable-experimental-feature NonescapableTypes \
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
// RUN: -enable-experimental-feature NoncopyableGenerics | %FileCheck %s


Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/raw_layout.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %target-swift-emit-silgen -enable-experimental-feature RawLayout %s | %FileCheck %s

// XFAIL: noncopyable_generics


// CHECK: @_rawLayout(size: 4, alignment: 4) @_moveOnly struct Lock
// CHECK: @_rawLayout(like: T) @_moveOnly struct Cell<T>
Expand Down
Loading