Skip to content

[BitwiseCopyable] Don't derive on @unchecked field #71242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 39 additions & 13 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/AST/Pattern.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeDifferenceVisitor.h"
#include "swift/AST/Types.h"
#include "swift/ClangImporter/ClangModule.h"
Expand Down Expand Up @@ -3009,6 +3010,21 @@ void TypeConverter::verifyLexicalLowering(const TypeLowering &lowering,
}
}

static bool isUnchecked(ProtocolConformanceRef conformance) {
if (!conformance)
return false;
if (!conformance.isConcrete())
return false;
auto concrete = conformance.getConcrete();
assert(concrete);
auto *root = concrete->getRootConformance();
assert(root);
auto *normal = dyn_cast<NormalProtocolConformance>(root);
if (!normal)
return false;
return normal->isUnchecked();
}

void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
AbstractionPattern origType,
CanType substType,
Expand Down Expand Up @@ -3042,10 +3058,17 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
if (!nominal)
return false;

// Nominals with generic parameters must be explicitly conformed to
// BitwiseCopyable.
auto *generic = ty.getAnyGeneric();
if (generic && generic->isGenericContext()) {
// Don't walk into types whose conformance is unchecked--such
// conformances obstruct automatic inference of BitwiseCopyable.
auto conformance = M.checkConformance(ty, bitwiseCopyableProtocol);
if (isUnchecked(conformance)) {
return true;
}

// Nominals with fields that conditionally conform to BitwiseCopyable
// must be explicitly conditionally conformed. Such a field must be a
// generic context.
if (nominal->isGenericContext()) {
return true;
}

Expand All @@ -3059,10 +3082,12 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
// Return false to indicate seeing a leaf which justifies the type
// being trivial but not conforming to BitwiseCopyable.

// A BitwiseCopyable conformer appearing within its layout doesn't
// explain why substType doesn't itself conform.
if (M.checkConformance(ty, bitwiseCopyableProtocol))
return true;
// A BitwiseCopyable conformer appearing within its layout explains a
// non-conformance iff the conformance is @unchecked.
auto conformance = M.checkConformance(ty, bitwiseCopyableProtocol);
if (conformance) {
return !isUnchecked(conformance);
}

// ModuleTypes are trivial but don't warrant being given a conformance
// to BitwiseCopyable.
Expand Down Expand Up @@ -3099,15 +3124,15 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
return true;
}

/// A non-conforming generic nominal type justifies substType not
/// conforming.
auto *generic = ty.getAnyGeneric();
if (generic && generic->isGenericContext()) {
/// A field of conditionally-BitwiseCopyable type justifies the
/// aggregate not conforming because the aggregate must be conformed
/// explicitly in that case.
if (nominal->isGenericContext()) {
return false;
}

// The field is trivial and the whole type is nonconforming. That's
// legal iff the type is public.
// legal iff the field's type is public.
return !nominal
->getFormalAccessScope(
/*useDC=*/nullptr,
Expand Down Expand Up @@ -3148,6 +3173,7 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
llvm::errs() << "Non-trivial type with _BitwiseCopyable conformance!?:\n"
<< substType << "\n";
conformance.print(llvm::errs());
llvm::errs() << "\n";
assert(false);
}
}
Expand Down
22 changes: 22 additions & 0 deletions lib/Sema/TypeCheckBitwise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class BitwiseCopyableStorageVisitor : public StorageVisitor {

bool visitMemberDecl(ValueDecl *storage, Type ty);
bool visitMemberType(Type type, SourceLoc loc);
bool isUnchecked(ProtocolConformanceRef conformance);
bool visitNonconformingMemberType(Type type, SourceLoc loc);
void emitNonconformingMemberTypeDiagnostic(Type ty, SourceLoc loc);
};
Expand Down Expand Up @@ -136,6 +137,13 @@ bool BitwiseCopyableStorageVisitor::visitMemberType(Type ty, SourceLoc loc) {
return visitNonconformingMemberType(ty, loc);
}

if (isImplicit(check) && isUnchecked(conformance)) {
// Do not automatically derive conformance if one of the field's
// conformance is @unchecked.
invalid = true;
return true;
}

// Walk the conformance, diagnosing any missing BitwiseCopyable conformances.
bool anyMissing = false;
conformance.forEachMissingConformance(
Expand All @@ -150,6 +158,20 @@ bool BitwiseCopyableStorageVisitor::visitMemberType(Type ty, SourceLoc loc) {
return anyMissing;
}

bool BitwiseCopyableStorageVisitor::isUnchecked(
ProtocolConformanceRef conformance) {
if (!conformance.isConcrete())
return false;
auto concrete = conformance.getConcrete();
assert(concrete);
auto *root = concrete->getRootConformance();
assert(root);
auto *normal = dyn_cast<NormalProtocolConformance>(root);
if (!normal)
return false;
return normal->isUnchecked();
}

bool BitwiseCopyableStorageVisitor::visitNonconformingMemberType(
Type ty, SourceLoc loc) {
if (ty->hasError())
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2165,7 +2165,8 @@ static void diagnoseConformanceImpliedByConditionalConformance(
/// to the given protocol. This should return true when @unchecked can be
/// used to disable those semantic checks.
static bool hasAdditionalSemanticChecks(ProtocolDecl *proto) {
return proto->isSpecificProtocol(KnownProtocolKind::Sendable);
return proto->isSpecificProtocol(KnownProtocolKind::Sendable) ||
proto->isSpecificProtocol(KnownProtocolKind::BitwiseCopyable);
}

/// Determine whether the type \c T conforms to the protocol \c Proto,
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/BridgeObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ public func _getBridgedNonVerbatimObjectiveCType<T>(_: T.Type) -> Any.Type?
/// already have writeback-scoped lifetime.
@frozen
public struct AutoreleasingUnsafeMutablePointer<Pointee /* TODO : class */>
: _Pointer {
: _Pointer, @unchecked _BitwiseCopyable {

public let _rawValue: Builtin.RawPointer

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/CTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public typealias CBool = Bool
/// Opaque pointers are used to represent C pointers to types that
/// cannot be represented in Swift, such as incomplete struct types.
@frozen
public struct OpaquePointer {
public struct OpaquePointer : @unchecked _BitwiseCopyable {
@usableFromInline
internal var _rawValue: Builtin.RawPointer

Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/core/Integers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1904,7 +1904,8 @@ public protocol _LosslessStringConvertibleOrNone {}
/// customization points for arithmetic operations. When you provide just those
/// methods, the standard library provides default implementations for all
/// other arithmetic methods and operators.
public protocol FixedWidthInteger: BinaryInteger, _LosslessStringConvertibleOrNone
public protocol FixedWidthInteger
: BinaryInteger, _LosslessStringConvertibleOrNone, _BitwiseCopyable
where Magnitude: FixedWidthInteger & UnsignedInteger,
Stride: FixedWidthInteger & SignedInteger {
/// The number of bits used for the underlying binary representation of
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/core/SIMDVector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ public protocol SIMDScalar : _BitwiseCopyable {
public protocol SIMD<Scalar>:
SIMDStorage,
Hashable,
ExpressibleByArrayLiteral
ExpressibleByArrayLiteral,
_BitwiseCopyable
{
/// The mask type resulting from pointwise comparisons of this vector type.
associatedtype MaskStorage: SIMD
Expand All @@ -95,7 +96,8 @@ public protocol SIMD<Scalar>:
Codable,
Hashable,
CustomStringConvertible,
ExpressibleByArrayLiteral
ExpressibleByArrayLiteral,
_BitwiseCopyable
{
/// The mask type resulting from pointwise comparisons of this vector type.
associatedtype MaskStorage: SIMD
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/core/UnsafeBufferPointer.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
/// collection with an `${Self}` instance copies the instances out of the
/// referenced memory and into the new collection.
@frozen // unsafe-performance
public struct Unsafe${Mutable}BufferPointer<Element> {
public struct Unsafe${Mutable}BufferPointer<Element>
: @unchecked _BitwiseCopyable {

@usableFromInline
let _position: Unsafe${Mutable}Pointer<Element>?
Expand Down
5 changes: 3 additions & 2 deletions stdlib/public/core/UnsafePointer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
/// let numberPointer = UnsafePointer<Int>(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen // unsafe-performance
public struct UnsafePointer<Pointee>: _Pointer {
public struct UnsafePointer<Pointee>: _Pointer, @unchecked _BitwiseCopyable {

/// A type that represents the distance between two pointers.
public typealias Distance = Int
Expand Down Expand Up @@ -578,7 +578,8 @@ public struct UnsafePointer<Pointee>: _Pointer {
/// let numberPointer = UnsafeMutablePointer<Int>(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen // unsafe-performance
public struct UnsafeMutablePointer<Pointee>: _Pointer {
public struct UnsafeMutablePointer<Pointee>
: _Pointer, @unchecked _BitwiseCopyable {

/// A type that represents the distance between two pointers.
public typealias Distance = Int
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/core/UnsafeRawPointer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
/// let numberPointer = UnsafeRawPointer(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen
public struct UnsafeRawPointer: _Pointer {
public struct UnsafeRawPointer: _Pointer, @unchecked _BitwiseCopyable {

public typealias Pointee = UInt8

Expand Down Expand Up @@ -727,7 +727,7 @@ extension UnsafeRawPointer {
/// let numberPointer = UnsafeMutableRawPointer(&number)
/// // Accessing 'numberPointer' is undefined behavior.
@frozen
public struct UnsafeMutableRawPointer: _Pointer {
public struct UnsafeMutableRawPointer: _Pointer, @unchecked _BitwiseCopyable {

public typealias Pointee = UInt8

Expand Down
15 changes: 15 additions & 0 deletions test/Sema/bitwise_copyable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ struct S_Explicit_With_Metatype_Optional_AnyObject : _BitwiseCopyable {
var ty: Optional<AnyObject>.Type
}

struct S_Unchecked : @unchecked _BitwiseCopyable {}

struct S_Implicit_With_S_Unchecked {
var s: S_Unchecked
}

func passS_Implicit_With_S_Unchecked(_ s: S_Implicit_With_S_Unchecked) {
take1(s) // expected-error{{type_does_not_conform_decl_owner}}
// expected-note@-92 {{where_requirement_failure_one_subst}}
}

struct S_Explicit_With_S_Unchecked : _BitwiseCopyable {
var s: S_Unchecked
}

//==============================================================================
//===========================DEPENDENCY-FREE TESTS=(END)======================}}
//==============================================================================
Expand Down
5 changes: 5 additions & 0 deletions test/SourceKit/DocSupport/doc_clang_module.swift.response
Original file line number Diff line number Diff line change
Expand Up @@ -5654,6 +5654,11 @@ var FooSubUnnamedEnumeratorA1: Int { get }
key.kind: source.lang.swift.ref.protocol,
key.name: "_Pointer",
key.usr: "s:s8_PointerP"
},
{
key.kind: source.lang.swift.ref.protocol,
key.name: "_BitwiseCopyable",
key.usr: "s:s16_BitwiseCopyableP"
}
]
},
Expand Down
14 changes: 7 additions & 7 deletions test/api-digester/Outputs/cake-abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,13 @@
"usr": "s:s25LosslessStringConvertibleP",
"mangledName": "$ss25LosslessStringConvertibleP"
},
{
"kind": "Conformance",
"name": "_BitwiseCopyable",
"printedName": "_BitwiseCopyable",
"usr": "s:s16_BitwiseCopyableP",
"mangledName": "$ss16_BitwiseCopyableP"
},
{
"kind": "Conformance",
"name": "SignedNumeric",
Expand Down Expand Up @@ -2022,13 +2029,6 @@
],
"usr": "s:s10SIMDScalarP",
"mangledName": "$ss10SIMDScalarP"
},
{
"kind": "Conformance",
"name": "_BitwiseCopyable",
"printedName": "_BitwiseCopyable",
"usr": "s:s16_BitwiseCopyableP",
"mangledName": "$ss16_BitwiseCopyableP"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ Func withoutActuallyEscaping(_:do:) is now without @rethrows

Protocol SIMDScalar has generic signature change from <Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar> to <Self : Swift._BitwiseCopyable, Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMDMaskScalar == Self.SIMDMaskScalar.SIMDMaskScalar, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar>
Protocol SIMDStorage has generic signature change from <Self.Scalar : Swift.Decodable, Self.Scalar : Swift.Encodable, Self.Scalar : Swift.Hashable> to <Self : Swift._BitwiseCopyable, Self.Scalar : Swift.Decodable, Self.Scalar : Swift.Encodable, Self.Scalar : Swift.Hashable>
Protocol FixedWidthInteger has generic signature change from <Self : Swift.BinaryInteger, Self : Swift.LosslessStringConvertible, Self.Magnitude : Swift.FixedWidthInteger, Self.Magnitude : Swift.UnsignedInteger, Self.Stride : Swift.FixedWidthInteger, Self.Stride : Swift.SignedInteger> to <Self : Swift.BinaryInteger, Self : Swift.LosslessStringConvertible, Self : Swift._BitwiseCopyable, Self.Magnitude : Swift.FixedWidthInteger, Self.Magnitude : Swift.UnsignedInteger, Self.Stride : Swift.FixedWidthInteger, Self.Stride : Swift.SignedInteger>
3 changes: 3 additions & 0 deletions test/api-digester/stability-stdlib-abi-without-asserts.test
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,8 @@ Protocol SIMDStorage has added inherited protocol _BitwiseCopyable
Protocol SIMDStorage has generic signature change from <Self.Scalar : Swift.Decodable, Self.Scalar : Swift.Encodable, Self.Scalar : Swift.Hashable> to <Self : Swift._BitwiseCopyable, Self.Scalar : Swift.Decodable, Self.Scalar : Swift.Encodable, Self.Scalar : Swift.Hashable>
Protocol _Pointer has added inherited protocol _BitwiseCopyable
Protocol _Pointer has generic signature change from <Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomReflectable, Self : Swift.Hashable, Self : Swift.Strideable> to <Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomReflectable, Self : Swift.Hashable, Self : Swift.Strideable, Self : Swift._BitwiseCopyable>
Protocol FixedWidthInteger has added inherited protocol _BitwiseCopyable
Protocol FixedWidthInteger has generic signature change from <Self : Swift.BinaryInteger, Self : Swift.LosslessStringConvertible, Self.Magnitude : Swift.FixedWidthInteger, Self.Magnitude : Swift.UnsignedInteger, Self.Stride : Swift.FixedWidthInteger, Self.Stride : Swift.SignedInteger> to <Self : Swift.BinaryInteger, Self : Swift.LosslessStringConvertible, Self : Swift._BitwiseCopyable, Self.Magnitude : Swift.FixedWidthInteger, Self.Magnitude : Swift.UnsignedInteger, Self.Stride : Swift.FixedWidthInteger, Self.Stride : Swift.SignedInteger>


// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)