mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
RequirementMachine: Add Copyable/Escapable requirements to 'placeholder' generic signatures
If we fail to build a generic signature (or requirement signature of a protocol) because of a request cycle or because Knuth-Bendix completion failed, we would create a placeholder signature with no requirements. However in a move-only world, a completely unconstrained generic parameter might generate spurious diagnostics when used in a copyable way. For this reason, let's outfit these placeholder signatures with a default set of conformance requirements to Copyable and Escapable.
This commit is contained in:
@@ -1301,9 +1301,10 @@ bool GenericContext::isComputingGenericSignature() const {
|
||||
|
||||
/// If we hit a cycle while building the generic signature, we can't return
|
||||
/// nullptr, since this breaks invariants elsewhere. Instead, build a dummy
|
||||
/// signature with no requirements.
|
||||
/// signature where everything is Copyable and Escapable, to avoid spurious
|
||||
/// downstream diagnostics concerning move-only types.
|
||||
static GenericSignature getPlaceholderGenericSignature(
|
||||
const DeclContext *DC) {
|
||||
ASTContext &ctx, const DeclContext *DC) {
|
||||
SmallVector<GenericParamList *, 2> gpLists;
|
||||
DC->forEachGenericContext([&](GenericParamList *genericParams) {
|
||||
gpLists.push_back(genericParams);
|
||||
@@ -1316,23 +1317,32 @@ static GenericSignature getPlaceholderGenericSignature(
|
||||
for (unsigned i : indices(gpLists))
|
||||
gpLists[i]->setDepth(i);
|
||||
|
||||
SmallVector<GenericTypeParamType *, 2> result;
|
||||
for (auto *genericParams : gpLists) {
|
||||
for (auto *genericParam : *genericParams) {
|
||||
result.push_back(genericParam->getDeclaredInterfaceType()
|
||||
->castTo<GenericTypeParamType>());
|
||||
SmallVector<GenericTypeParamType *, 2> genericParams;
|
||||
SmallVector<Requirement, 2> requirements;
|
||||
|
||||
for (auto *gpList : gpLists) {
|
||||
for (auto *genericParam : *gpList) {
|
||||
auto type = genericParam->getDeclaredInterfaceType();
|
||||
genericParams.push_back(type->castTo<GenericTypeParamType>());
|
||||
|
||||
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
|
||||
for (auto ip : InvertibleProtocolSet::full()) {
|
||||
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
|
||||
requirements.emplace_back(RequirementKind::Conformance, type,
|
||||
proto->getDeclaredInterfaceType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GenericSignature::get(result, {});
|
||||
return GenericSignature::get(genericParams, requirements);
|
||||
}
|
||||
|
||||
GenericSignature GenericContext::getGenericSignature() const {
|
||||
// Don't use evaluateOrDefault() here, because getting the 'default value'
|
||||
// is slightly expensive here so we don't want to do it eagerly.
|
||||
return getASTContext().evaluator(
|
||||
auto &ctx = getASTContext();
|
||||
return ctx.evaluator(
|
||||
GenericSignatureRequest{const_cast<GenericContext *>(this)},
|
||||
[this]() { return getPlaceholderGenericSignature(this); });
|
||||
[&ctx, this]() { return getPlaceholderGenericSignature(ctx, this); });
|
||||
}
|
||||
|
||||
GenericEnvironment *GenericContext::getGenericEnvironment() const {
|
||||
@@ -6912,10 +6922,46 @@ ProtocolDecl::getProtocolDependencies() const {
|
||||
llvm::None);
|
||||
}
|
||||
|
||||
/// If we hit a request cycle, give the protocol a requirement signature where
|
||||
/// everything is Copyable and Escapable. Otherwise, we'll get spurious
|
||||
/// downstream diagnostics concerning move-only types.
|
||||
static RequirementSignature getPlaceholderRequirementSignature(
|
||||
const ProtocolDecl *proto) {
|
||||
auto &ctx = proto->getASTContext();
|
||||
|
||||
SmallVector<Requirement, 2> requirements;
|
||||
|
||||
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
|
||||
auto add = [&](Type type) {
|
||||
for (auto ip : InvertibleProtocolSet::full()) {
|
||||
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
|
||||
requirements.emplace_back(RequirementKind::Conformance, type,
|
||||
proto->getDeclaredInterfaceType());
|
||||
}
|
||||
};
|
||||
|
||||
add(proto->getSelfInterfaceType());
|
||||
|
||||
for (auto *assocTypeDecl : proto->getAssociatedTypeMembers())
|
||||
add(assocTypeDecl->getDeclaredInterfaceType());
|
||||
}
|
||||
|
||||
// Maintain invariants.
|
||||
llvm::array_pod_sort(requirements.begin(), requirements.end(),
|
||||
[](const Requirement *lhs, const Requirement *rhs) -> int {
|
||||
return lhs->compare(*rhs);
|
||||
});
|
||||
|
||||
return RequirementSignature(ctx.AllocateCopy(requirements),
|
||||
ArrayRef<ProtocolTypeAlias>());
|
||||
}
|
||||
|
||||
RequirementSignature ProtocolDecl::getRequirementSignature() const {
|
||||
return evaluateOrDefault(getASTContext().evaluator,
|
||||
return getASTContext().evaluator(
|
||||
RequirementSignatureRequest { const_cast<ProtocolDecl *>(this) },
|
||||
RequirementSignature());
|
||||
[this]() {
|
||||
return getPlaceholderRequirementSignature(this);
|
||||
});
|
||||
}
|
||||
|
||||
bool ProtocolDecl::isComputingRequirementSignature() const {
|
||||
|
||||
@@ -346,6 +346,9 @@ void InverseRequirement::expandDefaults(
|
||||
ASTContext &ctx,
|
||||
ArrayRef<Type> gps,
|
||||
SmallVectorImpl<StructuralRequirement> &result) {
|
||||
if (!SWIFT_ENABLE_EXPERIMENTAL_NONCOPYABLE_GENERICS &&
|
||||
!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics))
|
||||
return;
|
||||
|
||||
SmallVector<ProtocolDecl*, NumInvertibleProtocols> defaults;
|
||||
expandDefaults(ctx, /*inverses=*/{}, defaults);
|
||||
|
||||
@@ -746,6 +746,25 @@ AbstractGenericSignatureRequest::evaluate(
|
||||
}
|
||||
}
|
||||
|
||||
/// If completion fails, build a dummy generic signature where everything is
|
||||
/// Copyable and Escapable, to avoid spurious downstream diagnostics
|
||||
/// concerning move-only types.
|
||||
static GenericSignature getPlaceholderGenericSignature(
|
||||
ASTContext &ctx, ArrayRef<GenericTypeParamType *> genericParams) {
|
||||
SmallVector<Requirement, 2> requirements;
|
||||
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
|
||||
for (auto param : genericParams) {
|
||||
for (auto ip : InvertibleProtocolSet::full()) {
|
||||
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
|
||||
requirements.emplace_back(RequirementKind::Conformance, param,
|
||||
proto->getDeclaredInterfaceType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GenericSignature::get(genericParams, requirements);
|
||||
}
|
||||
|
||||
GenericSignatureWithError
|
||||
InferredGenericSignatureRequest::evaluate(
|
||||
Evaluator &evaluator,
|
||||
@@ -857,7 +876,8 @@ InferredGenericSignatureRequest::evaluate(
|
||||
// generic parameters, as the outer parameters have already been expanded.
|
||||
SmallVector<Type, 4> paramTypes;
|
||||
if (allowInverses) {
|
||||
paramTypes.append(genericParams.begin() + numOuterParams, genericParams.end());
|
||||
paramTypes.append(genericParams.begin() + numOuterParams,
|
||||
genericParams.end());
|
||||
}
|
||||
|
||||
SmallVector<StructuralRequirement, 2> defaults;
|
||||
@@ -909,8 +929,7 @@ InferredGenericSignatureRequest::evaluate(
|
||||
diag::requirement_machine_completion_rule,
|
||||
rule);
|
||||
|
||||
auto result = GenericSignature::get(genericParams,
|
||||
parentSig.getRequirements());
|
||||
auto result = getPlaceholderGenericSignature(ctx, genericParams);
|
||||
|
||||
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
||||
rewriteCtx.endTimer("InferredGenericSignatureRequest");
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
|
||||
|
||||
// XFAIL: noncopyable_generics
|
||||
|
||||
protocol Fooable {
|
||||
associatedtype Foo // expected-note{{protocol requires nested type 'Foo'; add nested type 'Foo' for conformance}}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
|
||||
// XFAIL: noncopyable_generics
|
||||
|
||||
protocol ABA // expected-error {{cannot build rewrite system for protocol; rule length limit exceeded}}
|
||||
// expected-note@-1 {{failed rewrite rule is [ABA:A].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:A] => [ABA:A].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B].[ABA:B]}}
|
||||
where A.B == A.B.A { // expected-error *{{is not a member type}}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
|
||||
// XFAIL: noncopyable_generics
|
||||
|
||||
// https://github.com/apple/swift/issues/49119
|
||||
|
||||
protocol P {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// RUN: %target-typecheck-verify-swift
|
||||
|
||||
// XFAIL: noncopyable_generics
|
||||
// RUN: %target-typecheck-verify-swift -requirement-machine-max-rule-length=4
|
||||
|
||||
// https://github.com/apple/swift/issues/52031
|
||||
|
||||
@@ -14,7 +12,7 @@ protocol P {
|
||||
extension S: P where N: P {
|
||||
static func f<X: P>(_ x: X) -> S<X.A> where A == X, X.A == N {
|
||||
// expected-error@-1 {{cannot build rewrite system for generic signature; rule length limit exceeded}}
|
||||
// expected-note@-2 {{failed rewrite rule is τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[concrete: S<S<S<S<S<S<S<S<S<S<S<S<S<S<τ_0_0>>>>>>>>>>>>>>] => τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A] [subst↓]}}
|
||||
// expected-note@-2 {{τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A].[concrete: S<S<S<S<S<S<τ_0_0>>>>>>] => τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A] [subst↓]}}
|
||||
// expected-error@-3 {{'A' is not a member type of type 'X'}}
|
||||
// expected-error@-4 {{'A' is not a member type of type 'X'}}
|
||||
return S<X.A>()
|
||||
|
||||
Reference in New Issue
Block a user