AST: Use a builtin conformance for unconditional Copyable/Escapable

This generalizes what we were already doing for classes.
This commit is contained in:
Slava Pestov
2024-03-06 00:53:28 -05:00
parent 02c30d1c15
commit 41df661160
14 changed files with 47 additions and 79 deletions

View File

@@ -29,7 +29,6 @@
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/InverseMarking.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/PackConformance.h"
@@ -403,39 +402,6 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
}
static ProtocolConformanceRef
getBuiltinInvertibleProtocolConformance(NominalTypeDecl *nominal,
Type type,
ProtocolDecl *protocol) {
assert(isa<ClassDecl>(nominal));
ASTContext &ctx = protocol->getASTContext();
auto ip = protocol->getInvertibleProtocolKind();
switch (*ip) {
case InvertibleProtocolKind::Copyable:
// If move-only classes is enabled, we'll check the markings.
if (ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses)) {
switch (nominal->hasInverseMarking(*ip).getKind()) {
case InverseMarking::Kind::LegacyExplicit:
case InverseMarking::Kind::Explicit:
// An inverse ~Copyable prevents conformance.
return ProtocolConformanceRef::forInvalid();
case InverseMarking::Kind::None:
break;
}
}
break;
case InvertibleProtocolKind::Escapable:
// Always conforms.
break;
}
return ProtocolConformanceRef(
ctx.getBuiltinConformance(type, protocol,
BuiltinConformanceKind::Synthesized));
}
/// Synthesize a builtin type conformance to the given protocol, if
/// appropriate.
static ProtocolConformanceRef
@@ -625,13 +591,6 @@ LookupConformanceInModuleRequest::evaluate(
if (!nominal || isa<ProtocolDecl>(nominal))
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
// We specially avoid recording conformances to invertible protocols in a
// class's conformance table. This prevents an evaluator cycle.
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)
&& isa<ClassDecl>(nominal)
&& protocol->getInvertibleProtocolKind())
return getBuiltinInvertibleProtocolConformance(nominal, type, protocol);
// Expand conformances added by extension macros.
//
// FIXME: This expansion should only be done if the

View File

@@ -921,7 +921,22 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
// Form the conformance.
Type type = entry->getDeclContext()->getDeclaredInterfaceType();
ASTContext &ctx = nominal->getASTContext();
if (entry->getKind() == ConformanceEntryKind::Inherited) {
if (protocol->getInvertibleProtocolKind() &&
entry->getDeclContext() == nominal &&
(entry->getKind() == ConformanceEntryKind::Synthesized ||
entry->getKind() == ConformanceEntryKind::Inherited)) {
// Unconditional conformances to Copyable and Escapable are represented as
// builtin conformances, which do not need to store a substitution map.
//
// This avoids an exponential blowup when constructing the context
// substitution map for a type like G<G<G<G<...>>>>.
Type conformingType = nominal->getSelfInterfaceType();
entry->Conformance = ctx.getBuiltinConformance(
conformingType, protocol, BuiltinConformanceKind::Synthesized);
} else if (entry->getKind() == ConformanceEntryKind::Inherited) {
// For an inherited conformance, the conforming nominal type will
// be different from the nominal type.
assert(conformingNominal != nominal && "Broken inherited conformance");

View File

@@ -23,9 +23,9 @@
#include "swift/AST/FileUnit.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/InFlightSubstitution.h"
#include "swift/AST/InverseMarking.h"
#include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/PackConformance.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
@@ -1091,21 +1091,33 @@ void NominalTypeDecl::prepareConformanceTable() const {
// Synthesize the unconditional conformances to invertible protocols.
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
// Classes get their conformances during ModuleDecl::lookupConformance.
if (!isa<ClassDecl>(this)) {
bool missingOne = false;
for (auto ip : InvertibleProtocolSet::full()) {
if (!hasInverseMarking(ip))
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
else
missingOne = true;
}
// FIXME: We should be able to only resolve the inheritance clause once,
// but we also do it in ConformanceLookupTable::updateLookupTable().
InvertibleProtocolSet inverses;
bool anyObject = false;
(void) getDirectlyInheritedNominalTypeDecls(this, inverses, anyObject);
// Non-copyable and non-escaping types do not implicitly conform to
// any other protocols.
if (missingOne)
return;
// Handle deprecated attributes.
if (getAttrs().hasAttribute<MoveOnlyAttr>())
inverses.insert(InvertibleProtocolKind::Copyable);
if (getAttrs().hasAttribute<NonEscapableAttr>())
inverses.insert(InvertibleProtocolKind::Escapable);
bool hasSuppressedConformances = false;
for (auto ip : InvertibleProtocolSet::full()) {
if (!inverses.contains(ip) ||
(isa<ClassDecl>(this) &&
!ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses))) {
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
} else {
hasSuppressedConformances = true;
}
}
// Non-copyable and non-escaping types do not implicitly conform to
// any other protocols.
if (hasSuppressedConformances)
return;
} else if (!canBeCopyable()) {
return; // No synthesized conformances for move-only nominals.
}

View File

@@ -473,7 +473,7 @@ class InheritedProtocolCollector {
}
static bool isUncheckedConformance(ProtocolConformance *conformance) {
if (auto normal = conformance->getRootNormalConformance())
if (auto normal = dyn_cast<NormalProtocolConformance>(conformance->getRootConformance()))
return normal->isUnchecked();
return false;
}

View File

@@ -1,7 +1,5 @@
// RUN: %target-typecheck-verify-swift
// XFAIL: noncopyable_generics
func returnTuple1<each T>() -> (repeat each T) { fatalError() }
// expected-note@-1 {{in call to function 'returnTuple1()'}}

View File

@@ -1,8 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking 2>&1 %s
// XFAIL: noncopyable_generics
// REQUIRES: concurrency
// REQUIRES: distributed
@@ -50,4 +48,4 @@ public final class CompletelyHollowActorSystem_NotEvenTypes: DistributedActorSys
// expected-error@-3{{class 'CompletelyHollowActorSystem_NotEvenTypes' is missing witness for protocol requirement 'remoteCall'}}
// expected-note@-4{{add stubs for conformance}}
// expected-note@-5{{add stubs for conformance}}
}
}

View File

@@ -19,7 +19,7 @@
// CHECK-LABEL: .Outer.InnerStruct.init()@
// CHECK: Generic signature: <A, C where A : Escapable, C : Escapable>
// CHECK: (normal_conformance type="Outer<A>.InnerStruct<C>" protocol="Escapable")
// CHECK: (builtin_conformance type="Outer<A>.InnerStruct<C>" protocol="Escapable")
// CHECK-LABEL: .Outer.InnerVariation1@
// CHECK: Generic signature: <A, D where A : Escapable, D : Escapable>

View File

@@ -1,8 +1,5 @@
// RUN: %target-typecheck-verify-swift
// rdar://122287787 (NCGenerics performance issues in regression tests)
// UNSUPPORTED: noncopyable_generics
// This needs a better diagnostic. The real problem is the 'C == G<I>'
// requirement in P2 conflicts with the one in P1.

View File

@@ -1,8 +1,5 @@
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -sil-print-generic-specialization-loops %s 2>&1 | %FileCheck --check-prefix=CHECK %s
// rdar://122287787 (NCGenerics performance issues in regression tests)
// UNSUPPORTED: noncopyable_generics
// Check that the generic specializer does not hang a compiler by
// creating and infinite loop of generic specializations.

View File

@@ -1,8 +1,5 @@
// RUN: %target-typecheck-verify-swift
// rdar://122287787 (NCGenerics performance issues in regression tests)
// UNSUPPORTED: noncopyable_generics
struct SelfRecursiveStruct {
let a: SelfRecursiveStruct // expected-error{{value type 'SelfRecursiveStruct' cannot have a stored property that recursively contains it}}
}

View File

@@ -1,8 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/mods)
// XFAIL: noncopyable_generics
// RUN: touch %t/empty.swift
// RUN: %{python} %utils/split_file.py -o %t %s

View File

@@ -1,5 +1,7 @@
// REQUIRES: VENDOR=apple
// XFAIL: noncopyable_generics
// RUN: %empty-directory(%t.mod)
// RUN: %empty-directory(%t.sdk)
// RUN: %empty-directory(%t.module-cache)

View File

@@ -1,8 +1,5 @@
// RUN: %target-typecheck-verify-swift
// rdar://122287787 (NCGenerics performance issues in regression tests)
// UNSUPPORTED: noncopyable_generics
protocol SomeProtocol {
associatedtype T
}

View File

@@ -1,7 +1,5 @@
// RUN: %target-typecheck-verify-swift
// XFAIL: noncopyable_generics
public enum E : E.R {
// expected-error@-1 {{'E' has a raw type that depends on itself}}
// expected-note@-2 2{{through reference here}}