[BitwiseCopyable] Infer and check constraint.

When the BitwiseCopyable experimental feature is enabled, infer types to
conform to `_BitwiseCopyable`.  The `_BitwiseCopyable` inference broadly
follows the approach taken to infer `Sendable`.

(1) Special types are conformed:
- function types if trivial
- metatypes
- builtin types if trivial

(2) TheTupleType is conditionally conformed.

(3) Nominal types are conformed if:
- non-public or public+fixed-layout
- enum or struct (non-class)
- every field conforms to _BitwiseCopyable

Additionally, check that nominal types which are explicitly conformed to
`_BitwiseCopyable` satisfy the latter two conditions of (3).

For a public, non-fixed-layout type to conform to `_BitwiseCopyable`,
the user must conform the type explicitly.

Finally, verify that conformances correspond to TypeLowering's notion of
triviality to the appropriate extent:
- if a type isn't trivial, it doesn't conform to `_BitwiseCopyable`
  unless it's an archetype
- if a type is trivial, it conforms to `_BitwiseCopyable` unless some
  field in its layout doesn't conform to `_BitwiseCopyable`, which is
  only permitted under certain circumstances (the type has generic
  parameters, the type is public non-fixed-layout, the type is a
  reference but has ReferenceStorage::Unmanaged, the type is a
  ModuleType, etc.)
This commit is contained in:
Nate Chandler
2024-01-09 08:05:49 -08:00
parent b98679bfb6
commit bac9e94a1d
23 changed files with 1022 additions and 8 deletions

View File

@@ -1808,6 +1808,34 @@ static bool isEscapableFunctionType(EitherFunctionType eitherFnTy) {
return !functionType->isNoEscape();
}
static bool isBitwiseCopyableFunctionType(EitherFunctionType eitherFnTy) {
SILFunctionTypeRepresentation representation;
if (auto silFnTy = eitherFnTy.dyn_cast<const SILFunctionType *>()) {
representation = silFnTy->getRepresentation();
} else {
auto fnTy = eitherFnTy.get<const FunctionType *>();
representation = convertRepresentation(fnTy->getRepresentation());
}
switch (representation) {
case SILFunctionTypeRepresentation::Thick:
case SILFunctionTypeRepresentation::Block:
return false;
case SILFunctionTypeRepresentation::Thin:
case SILFunctionTypeRepresentation::CXXMethod:
case SILFunctionTypeRepresentation::CFunctionPointer:
case SILFunctionTypeRepresentation::Method:
case SILFunctionTypeRepresentation::ObjCMethod:
case SILFunctionTypeRepresentation::WitnessMethod:
case SILFunctionTypeRepresentation::Closure:
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
return true;
}
}
/// Synthesize a builtin function type conformance to the given protocol, if
/// appropriate.
static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
@@ -1835,6 +1863,10 @@ static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
// Functions cannot permanently destroy a move-only var/let
// that they capture, so it's safe to copy functions, like classes.
return synthesizeConformance();
case KnownProtocolKind::BitwiseCopyable:
if (isBitwiseCopyableFunctionType(functionType))
return synthesizeConformance();
break;
default:
break;
}
@@ -1861,12 +1893,13 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
}
}
// All metatypes are Sendable, Copyable, and Escapable.
// All metatypes are Sendable, Copyable, Escapable, and BitwiseCopyable.
if (auto kp = protocol->getKnownProtocolKind()) {
switch (*kp) {
case KnownProtocolKind::Sendable:
case KnownProtocolKind::Copyable:
case KnownProtocolKind::Escapable:
case KnownProtocolKind::BitwiseCopyable:
return ProtocolConformanceRef(
ctx.getBuiltinConformance(type, protocol,
BuiltinConformanceKind::Synthesized));
@@ -1880,11 +1913,12 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
/// Synthesize a builtin type conformance to the given protocol, if
/// appropriate.
static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) {
// All builtin are Sendable, Copyable, and Escapable
static ProtocolConformanceRef
getBuiltinBuiltinTypeConformance(Type type, const BuiltinType *builtinType,
ProtocolDecl *protocol) {
if (auto kp = protocol->getKnownProtocolKind()) {
switch (*kp) {
// All builtin types are Sendable, Copyable, and Escapable.
case KnownProtocolKind::Sendable:
case KnownProtocolKind::Copyable:
case KnownProtocolKind::Escapable: {
@@ -1893,6 +1927,15 @@ static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
ctx.getBuiltinConformance(type, protocol,
BuiltinConformanceKind::Synthesized));
}
// Some builtin types are BitwiseCopyable.
case KnownProtocolKind::BitwiseCopyable: {
if (builtinType->isBitwiseCopyable()) {
ASTContext &ctx = protocol->getASTContext();
return ProtocolConformanceRef(ctx.getBuiltinConformance(
type, protocol, BuiltinConformanceKind::Synthesized));
}
break;
}
default:
break;
}
@@ -2116,6 +2159,18 @@ LookupConformanceInModuleRequest::evaluate(
} else {
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
}
} else if (protocol->isSpecificProtocol(
KnownProtocolKind::BitwiseCopyable)) {
// Try to infer BitwiseCopyable conformance.
ImplicitKnownProtocolConformanceRequest request{
nominal, KnownProtocolKind::BitwiseCopyable};
if (auto conformance =
evaluateOrDefault(ctx.evaluator, request, nullptr)) {
conformances.clear();
conformances.push_back(conformance);
} else {
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
}
} else {
// Was unable to infer the missing conformance.
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);