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:
Slava Pestov
2024-02-06 22:55:42 -05:00
parent ff3ea642b1
commit e7d7f6f69f
7 changed files with 87 additions and 27 deletions

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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");

View File

@@ -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}}

View File

@@ -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}}

View File

@@ -1,7 +1,5 @@
// RUN: %target-typecheck-verify-swift
// XFAIL: noncopyable_generics
// https://github.com/apple/swift/issues/49119
protocol P {

View File

@@ -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>()