Skip to content

Commit bac9e94

Browse files
committed
[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.)
1 parent b98679b commit bac9e94

23 files changed

+1022
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7576,6 +7576,24 @@ NOTE(add_explicit_protocol_for_conformance,none,
75767576
ERROR(escapable_requires_feature_flag,none,
75777577
"type '~Escapable' requires -enable-experimental-feature NonescapableTypes",
75787578
())
7579+
ERROR(non_bitwise_copyable_type_class,none,
7580+
"class cannot conform to 'BitwiseCopyable'", ())
7581+
ERROR(non_bitwise_copyable_type_member,none,
7582+
"%select{stored property %2|associated value %2}1 of "
7583+
"'BitwiseCopyable'-conforming %kind3 has non-bitwise-copyable type %0",
7584+
(Type, bool, DeclName, const ValueDecl *))
7585+
NOTE(non_bitwise_copyable_nominal,none,
7586+
"%kind0 does not conform to the 'BitwiseCopyable' protocol",
7587+
(const ValueDecl *))
7588+
NOTE(non_bitwise_copyable_function_type,none,
7589+
"a function type must be marked '@convention(thin)' or '@convention(c)' to be bitwise copyable",
7590+
())
7591+
NOTE(add_nominal_bitwise_copyable_conformance,none,
7592+
"consider making %kind0 conform to the 'BitwiseCopyable' protocol",
7593+
(const ValueDecl *))
7594+
NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
7595+
"consider making generic parameter %0 conform to the 'BitwiseCopyable' protocol",
7596+
(Type))
75797597

75807598
// -- older ones below --
75817599

include/swift/AST/Types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,7 @@ class BuiltinType : public TypeBase {
16401640
bool prependBuiltinNamespace = true) const;
16411641

16421642
BuiltinTypeKind getBuiltinTypeKind() const;
1643+
bool isBitwiseCopyable() const;
16431644
};
16441645
DEFINE_EMPTY_CAN_TYPE_WRAPPER(BuiltinType, Type)
16451646

include/swift/SIL/TypeLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,9 @@ class TypeConverter {
12851285
void verifyLexicalLowering(const TypeLowering &, AbstractionPattern origType,
12861286
CanType origSubstType,
12871287
TypeExpansionContext forExpansion);
1288+
void verifyTrivialLowering(const TypeLowering &, AbstractionPattern origType,
1289+
CanType origSubstType,
1290+
TypeExpansionContext forExpansion);
12881291
bool visitAggregateLeaves(
12891292
Lowering::AbstractionPattern origType, CanType substType,
12901293
TypeExpansionContext context,

lib/AST/ASTContext.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6295,6 +6295,9 @@ BuiltinTupleDecl *ASTContext::getBuiltinTupleDecl() {
62956295
if (auto *proto = getProtocol(KnownProtocolKind::Escapable))
62966296
buildFakeExtension(proto);
62976297

6298+
if (auto *proto = getProtocol(KnownProtocolKind::BitwiseCopyable))
6299+
buildFakeExtension(proto);
6300+
62986301
return result;
62996302
}
63006303

lib/AST/Builtins.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,6 +3092,27 @@ BuiltinTypeKind BuiltinType::getBuiltinTypeKind() const {
30923092
return BuiltinTypeKind(std::underlying_type<TypeKind>::type(getKind()));
30933093
}
30943094

3095+
bool BuiltinType::isBitwiseCopyable() const {
3096+
switch (getBuiltinTypeKind()) {
3097+
case BuiltinTypeKind::BuiltinInteger:
3098+
case BuiltinTypeKind::BuiltinIntegerLiteral:
3099+
case BuiltinTypeKind::BuiltinFloat:
3100+
case BuiltinTypeKind::BuiltinPackIndex:
3101+
case BuiltinTypeKind::BuiltinRawPointer:
3102+
case BuiltinTypeKind::BuiltinVector:
3103+
case BuiltinTypeKind::BuiltinExecutor:
3104+
case BuiltinTypeKind::BuiltinJob:
3105+
case BuiltinTypeKind::BuiltinRawUnsafeContinuation:
3106+
return true;
3107+
case BuiltinTypeKind::BuiltinNativeObject:
3108+
case BuiltinTypeKind::BuiltinBridgeObject:
3109+
case BuiltinTypeKind::BuiltinUnsafeValueBuffer:
3110+
case BuiltinTypeKind::BuiltinDefaultActorStorage:
3111+
case BuiltinTypeKind::BuiltinNonDefaultDistributedActorStorage:
3112+
return false;
3113+
}
3114+
}
3115+
30953116
StringRef BuiltinType::getTypeName(SmallVectorImpl<char> &result,
30963117
bool prependBuiltinNamespace) const {
30973118
#ifdef MAYBE_GET_NAMESPACED_BUILTIN

lib/AST/Module.cpp

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,34 @@ static bool isEscapableFunctionType(EitherFunctionType eitherFnTy) {
18081808
return !functionType->isNoEscape();
18091809
}
18101810

1811+
static bool isBitwiseCopyableFunctionType(EitherFunctionType eitherFnTy) {
1812+
SILFunctionTypeRepresentation representation;
1813+
if (auto silFnTy = eitherFnTy.dyn_cast<const SILFunctionType *>()) {
1814+
representation = silFnTy->getRepresentation();
1815+
} else {
1816+
auto fnTy = eitherFnTy.get<const FunctionType *>();
1817+
representation = convertRepresentation(fnTy->getRepresentation());
1818+
}
1819+
1820+
switch (representation) {
1821+
case SILFunctionTypeRepresentation::Thick:
1822+
case SILFunctionTypeRepresentation::Block:
1823+
return false;
1824+
case SILFunctionTypeRepresentation::Thin:
1825+
case SILFunctionTypeRepresentation::CXXMethod:
1826+
case SILFunctionTypeRepresentation::CFunctionPointer:
1827+
case SILFunctionTypeRepresentation::Method:
1828+
case SILFunctionTypeRepresentation::ObjCMethod:
1829+
case SILFunctionTypeRepresentation::WitnessMethod:
1830+
case SILFunctionTypeRepresentation::Closure:
1831+
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
1832+
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
1833+
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
1834+
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
1835+
return true;
1836+
}
1837+
}
1838+
18111839
/// Synthesize a builtin function type conformance to the given protocol, if
18121840
/// appropriate.
18131841
static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
@@ -1835,6 +1863,10 @@ static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
18351863
// Functions cannot permanently destroy a move-only var/let
18361864
// that they capture, so it's safe to copy functions, like classes.
18371865
return synthesizeConformance();
1866+
case KnownProtocolKind::BitwiseCopyable:
1867+
if (isBitwiseCopyableFunctionType(functionType))
1868+
return synthesizeConformance();
1869+
break;
18381870
default:
18391871
break;
18401872
}
@@ -1861,12 +1893,13 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
18611893
}
18621894
}
18631895

1864-
// All metatypes are Sendable, Copyable, and Escapable.
1896+
// All metatypes are Sendable, Copyable, Escapable, and BitwiseCopyable.
18651897
if (auto kp = protocol->getKnownProtocolKind()) {
18661898
switch (*kp) {
18671899
case KnownProtocolKind::Sendable:
18681900
case KnownProtocolKind::Copyable:
18691901
case KnownProtocolKind::Escapable:
1902+
case KnownProtocolKind::BitwiseCopyable:
18701903
return ProtocolConformanceRef(
18711904
ctx.getBuiltinConformance(type, protocol,
18721905
BuiltinConformanceKind::Synthesized));
@@ -1880,11 +1913,12 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
18801913

18811914
/// Synthesize a builtin type conformance to the given protocol, if
18821915
/// appropriate.
1883-
static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
1884-
Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) {
1885-
// All builtin are Sendable, Copyable, and Escapable
1916+
static ProtocolConformanceRef
1917+
getBuiltinBuiltinTypeConformance(Type type, const BuiltinType *builtinType,
1918+
ProtocolDecl *protocol) {
18861919
if (auto kp = protocol->getKnownProtocolKind()) {
18871920
switch (*kp) {
1921+
// All builtin types are Sendable, Copyable, and Escapable.
18881922
case KnownProtocolKind::Sendable:
18891923
case KnownProtocolKind::Copyable:
18901924
case KnownProtocolKind::Escapable: {
@@ -1893,6 +1927,15 @@ static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
18931927
ctx.getBuiltinConformance(type, protocol,
18941928
BuiltinConformanceKind::Synthesized));
18951929
}
1930+
// Some builtin types are BitwiseCopyable.
1931+
case KnownProtocolKind::BitwiseCopyable: {
1932+
if (builtinType->isBitwiseCopyable()) {
1933+
ASTContext &ctx = protocol->getASTContext();
1934+
return ProtocolConformanceRef(ctx.getBuiltinConformance(
1935+
type, protocol, BuiltinConformanceKind::Synthesized));
1936+
}
1937+
break;
1938+
}
18961939
default:
18971940
break;
18981941
}
@@ -2116,6 +2159,18 @@ LookupConformanceInModuleRequest::evaluate(
21162159
} else {
21172160
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
21182161
}
2162+
} else if (protocol->isSpecificProtocol(
2163+
KnownProtocolKind::BitwiseCopyable)) {
2164+
// Try to infer BitwiseCopyable conformance.
2165+
ImplicitKnownProtocolConformanceRequest request{
2166+
nominal, KnownProtocolKind::BitwiseCopyable};
2167+
if (auto conformance =
2168+
evaluateOrDefault(ctx.evaluator, request, nullptr)) {
2169+
conformances.clear();
2170+
conformances.push_back(conformance);
2171+
} else {
2172+
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
2173+
}
21192174
} else {
21202175
// Was unable to infer the missing conformance.
21212176
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,11 @@ static SmallVector<ProtocolConformance *, 2> findSynthesizedConformances(
12581258
trySynthesize(KnownProtocolKind::Copyable);
12591259
trySynthesize(KnownProtocolKind::Escapable);
12601260
}
1261+
1262+
if (nominal->getASTContext().LangOpts.hasFeature(
1263+
Feature::BitwiseCopyable)) {
1264+
trySynthesize(KnownProtocolKind::BitwiseCopyable);
1265+
}
12611266
}
12621267

12631268
/// Distributed actors can synthesize Encodable/Decodable, so look for those

lib/SIL/IR/TypeLowering.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,6 +2942,7 @@ void TypeConverter::verifyLowering(const TypeLowering &lowering,
29422942
CanType substType,
29432943
TypeExpansionContext forExpansion) {
29442944
verifyLexicalLowering(lowering, origType, substType, forExpansion);
2945+
verifyTrivialLowering(lowering, origType, substType, forExpansion);
29452946
}
29462947

29472948
void TypeConverter::verifyLexicalLowering(const TypeLowering &lowering,
@@ -3014,6 +3015,148 @@ void TypeConverter::verifyLexicalLowering(const TypeLowering &lowering,
30143015
"Found non-trivial lexical leaf in non-trivial non-lexical type?!");
30153016
}
30163017
}
3018+
3019+
void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
3020+
AbstractionPattern origType,
3021+
CanType substType,
3022+
TypeExpansionContext forExpansion) {
3023+
if (!Context.LangOpts.hasFeature(Feature::BitwiseCopyable))
3024+
return;
3025+
auto *bitwiseCopyableProtocol =
3026+
Context.getProtocol(KnownProtocolKind::BitwiseCopyable);
3027+
if (!bitwiseCopyableProtocol)
3028+
return;
3029+
3030+
auto conformance = M.conformsToProtocol(substType, bitwiseCopyableProtocol);
3031+
3032+
if (lowering.isTrivial() && !conformance) {
3033+
// A trivial type can only lack a conformance if one of its leaves is a
3034+
// public type that doesn't conform.
3035+
bool hasNoNonconformingNode = visitAggregateLeaves(
3036+
origType, substType, forExpansion,
3037+
/*isLeaf=*/
3038+
[&](auto ty, auto origTy, auto *field, auto index) -> bool {
3039+
// The field's type is an aggregate. Treat it as a leaf if it is
3040+
// public.
3041+
auto *nominal = ty.getAnyNominal();
3042+
// Non-nominal types are public iff their components are; visit the
3043+
// components.
3044+
if (!nominal)
3045+
return false;
3046+
3047+
// Nominals with generic parameters must be explicitly conformed to
3048+
// BitwiseCopyable.
3049+
auto *generic = ty.getAnyGeneric();
3050+
if (generic && generic->isGenericContext()) {
3051+
return true;
3052+
}
3053+
3054+
return nominal
3055+
->getFormalAccessScope(/*useDC=*/nullptr,
3056+
/*treatUsableFromInlineAsPublic=*/true)
3057+
.isPublic();
3058+
},
3059+
/*visit=*/
3060+
[&](auto ty, auto origTy, auto *field, auto index) -> bool {
3061+
// Return false to indicate seeing a leaf which justifies the type
3062+
// being trivial but not conforming to BitwiseCopyable.
3063+
3064+
// A BitwiseCopyable conformer appearing within its layout doesn't
3065+
// explain why substType doesn't itself conform.
3066+
if (M.conformsToProtocol(ty, bitwiseCopyableProtocol))
3067+
return true;
3068+
3069+
// ModuleTypes are trivial but don't warrant being given a conformance
3070+
// to BitwiseCopyable.
3071+
if (auto mt = dyn_cast<ModuleType>(ty)) {
3072+
// If one were to appear within a type, its lack of conformance
3073+
// wouldn't result in the type's failure to conform. Conversely, if
3074+
// substType itself is the ModuleType, it is trivial and
3075+
// non-conformant; return false to indicate its the leaf which
3076+
// justifies substType being non-conformant.
3077+
return field;
3078+
}
3079+
3080+
// Given a non-bitwise-copyable C, unowned(unsafe) C is still
3081+
// BitwiseCopyable.
3082+
auto rst = dyn_cast<ReferenceStorageType>(ty);
3083+
if (rst && rst->getOwnership() == ReferenceOwnership::Unmanaged) {
3084+
// If a ReferenceStorageType appears as a component of an aggregate,
3085+
// that shouldn't stop the aggregate from being BitwiseCopyable. If
3086+
// the whole type is the ReferenceStorageType, however, it does not
3087+
// conform.
3088+
return field;
3089+
}
3090+
3091+
auto *nominal = ty.getAnyNominal();
3092+
3093+
// Non-nominal types are trivial iff conforming.
3094+
if (!nominal) {
3095+
llvm::errs()
3096+
<< "Non-nominal type without conformance to _BitwiseCopyable:\n"
3097+
<< ty << "\n"
3098+
<< "within " << substType << "\n"
3099+
<< "of " << origType << "\n";
3100+
assert(false);
3101+
return true;
3102+
}
3103+
3104+
/// A non-conforming generic nominal type justifies substType not
3105+
/// conforming.
3106+
auto *generic = ty.getAnyGeneric();
3107+
if (generic && generic->isGenericContext()) {
3108+
return false;
3109+
}
3110+
3111+
// The field is trivial and the whole type is nonconforming. That's
3112+
// legal iff the type is public.
3113+
return !nominal
3114+
->getFormalAccessScope(
3115+
/*useDC=*/nullptr,
3116+
/*treatUsableFromInlineAsPublic=*/true)
3117+
.isPublic();
3118+
});
3119+
if (hasNoNonconformingNode) {
3120+
llvm::errs() << "Trivial type without a BitwiseCopyable conformance!?:\n"
3121+
<< substType << "\n"
3122+
<< "of " << origType << "\n";
3123+
assert(false);
3124+
}
3125+
}
3126+
3127+
if (!lowering.isTrivial() && conformance) {
3128+
// A non-trivial type can only conform if at least one of its leaves is a
3129+
// type parameter that conforms.
3130+
bool hasNoConformingArchetypeNode = visitAggregateLeaves(
3131+
origType, substType, forExpansion,
3132+
/*isLeaf=*/
3133+
[&](auto ty, auto origTy, auto *field, auto index) -> bool {
3134+
// Walk into every aggregate.
3135+
return false;
3136+
},
3137+
/*visit=*/
3138+
[&](auto ty, auto origTy, auto *field, auto index) -> bool {
3139+
// Return false to indicate visiting a type parameter.
3140+
3141+
if (origTy.isTypeParameter())
3142+
return false;
3143+
3144+
// Unfortunately, the type parameter's conformance may not be visible
3145+
// here.
3146+
assert(M.conformsToProtocol(ty, bitwiseCopyableProtocol) &&
3147+
"leaf of non-trivial BitwiseCopyable type that doesn't "
3148+
"conform to BitwiseCopyable!?");
3149+
3150+
return true;
3151+
});
3152+
if (hasNoConformingArchetypeNode) {
3153+
llvm::errs() << "Non-trivial type with _BitwiseCopyable conformance!?:\n"
3154+
<< substType << "\n";
3155+
conformance.print(llvm::errs());
3156+
assert(false);
3157+
}
3158+
}
3159+
}
30173160
#endif
30183161

30193162
CanType

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ add_swift_host_library(swiftSema STATIC
4545
TypeCheckAccess.cpp
4646
TypeCheckAttr.cpp
4747
TypeCheckAvailability.cpp
48+
TypeCheckBitwise.cpp
4849
TypeCheckCaptures.cpp
4950
TypeCheckCircularity.cpp
5051
TypeCheckCodeCompletion.cpp

0 commit comments

Comments
 (0)