Skip to content

IRGen: Make copying value witnesses trap for noncopyable types. #64194

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 1 commit into from
Mar 8, 2023
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
48 changes: 48 additions & 0 deletions lib/IRGen/GenValueWitness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,17 @@ static llvm::Constant *getNoOpVoidFunction(IRGenModule &IGM) {
});
}

/// Return a function that traps because of an attempt to copy a noncopyable
/// type.
static llvm::Constant *getNoncopyableTrapFunction(IRGenModule &IGM) {
return IGM.getOrCreateHelperFunction("__swift_cannot_copy_noncopyable_type",
IGM.VoidTy, {},
[&](IRGenFunction &IGF) {
IGF.Builder.CreateNonMergeableTrap(IGM, "attempt to copy a value of a type that cannot be copied");
IGF.Builder.CreateUnreachable();
});
}

/// Return a function which takes three pointer arguments and does a
/// retaining assignWithCopy on the first two: it loads a pointer from
/// the second, retains it, loads a pointer from the first, stores the
Expand Down Expand Up @@ -918,6 +929,33 @@ bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM,
return false;
}

static bool
valueWitnessRequiresCopyability(ValueWitness index) {
switch (index) {
case ValueWitness::InitializeBufferWithCopyOfBuffer:
case ValueWitness::InitializeWithCopy:
case ValueWitness::AssignWithCopy:
return true;

case ValueWitness::Destroy:
case ValueWitness::InitializeWithTake:
case ValueWitness::AssignWithTake:
case ValueWitness::GetEnumTagSinglePayload:
case ValueWitness::StoreEnumTagSinglePayload:
case ValueWitness::Size:
case ValueWitness::Stride:
case ValueWitness::Flags:
case ValueWitness::ExtraInhabitantCount:
case ValueWitness::GetEnumTag:
case ValueWitness::DestructiveProjectEnumData:
case ValueWitness::DestructiveInjectEnumTag:
return false;
}
llvm_unreachable("not all value witnesses covered");
}



/// Find a witness to the fact that a type is a value type.
/// Always adds an i8*.
static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B,
Expand All @@ -931,6 +969,16 @@ static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B,
B.addSignedPointer(fn, IGM.getOptions().PointerAuth.ValueWitnesses, index);
};

// Copyable and noncopyable types share a value witness table layout. It
// should normally be statically impossible to generate a call to a copy
// value witness, but if somebody manages to dynamically get hold of metadata
// for a noncopyable type, and tries to use it to copy values of that type,
// we should trap to prevent the attempt.
if (!concreteTI.isCopyable(ResilienceExpansion::Maximal)
&& valueWitnessRequiresCopyability(index)) {
return addFunction(getNoncopyableTrapFunction(IGM));
}

// Try to use a standard function.
switch (index) {
case ValueWitness::Destroy:
Expand Down
12 changes: 11 additions & 1 deletion test/IRGen/moveonly_deinit.sil
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sil_stage canonical
class C {}

// CHECK-LABEL: @{{.*}}8MOStructVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x81_0007>
// CHECK-32-SAME: <i32 0x81_0003>
Expand All @@ -27,6 +28,7 @@ struct MOStruct {
}

// CHECK-LABEL: @{{.*}}6MOEnumOWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, enum, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0xA1_0007>
// CHECK-32-SAME: <i32 0xA1_0003>
Expand All @@ -39,6 +41,7 @@ enum MOEnum {
}

// CHECK-LABEL: @{{.*}}13MOComboStructVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, non-inline, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x83_0007>
// CHECK-32-SAME: <i32 0x83_0003>
Expand All @@ -50,6 +53,7 @@ struct MOComboStruct {
}

// CHECK-LABEL: @{{.*}}11MOComboEnumOWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, enum, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0xA1_0007>
// CHECK-32-SAME: <i32 0xA1_0003>
Expand All @@ -64,6 +68,7 @@ enum MOComboEnum {
// common layout types.

// CHECK-LABEL: @{{.*}}13MOEmptyStructVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, nontrivial deinit, byte aligned
// CHECK-SAME: <i32 0x81_0000>
@_moveOnly
Expand All @@ -72,6 +77,7 @@ struct MOEmptyStruct {
}

// CHECK-LABEL: @{{.*}}15MOIntLikeStructVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x81_0007>
// CHECK-32-SAME: <i32 0x81_0003>
Expand All @@ -83,6 +89,7 @@ struct MOIntLikeStruct {
}

// CHECK-LABEL: @{{.*}}26MOSingleRefcountLikeStructVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x81_0007>
// CHECK-32-SAME: <i32 0x81_0003>
Expand All @@ -96,13 +103,15 @@ struct MOSingleRefcountLikeStruct {
// Even if they don't have deinits, we shouldn't share a vwt.

// CHECK-LABEL: @{{.*}}21MOEmptyStructNoDeinitVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, trivial deinit, byte aligned
// CHECK-SAME: <i32 0x80_0000>
@_moveOnly
struct MOEmptyStructNoDeinit {
}

// CHECK-LABEL: @{{.*}}23MOIntLikeStructNoDeinitVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, trivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x80_0007>
// CHECK-32-SAME: <i32 0x80_0003>
Expand All @@ -112,6 +121,7 @@ struct MOIntLikeStructNoDeinit {
}

// CHECK-LABEL: @{{.*}}34MOSingleRefcountLikeStructNoDeinitVWV" =
// CHECK-SAME: @__swift_cannot_copy_noncopyable_type
// noncopyable, nontrivial deinit, pointer aligned
// CHECK-64-SAME: <i32 0x81_0007>
// CHECK-32-SAME: <i32 0x81_0003>
Expand Down Expand Up @@ -319,7 +329,7 @@ sil_moveonlydeinit MOGenericDeinit { @destroy_generic }
// CHECK-LABEL: define {{.*}} @"{{.*}}13MOComboStructVwxx"
// CHECK: call {{.*}} @destroy_struct(
// CHECK: call {{.*}} @destroy_enum(
// CHECK: call {{.*}} @swift_retain(
// CHECK: call {{.*}} @swift_release
// CHECK-LABEL: define {{.*}} @"{{.*}}11MOComboEnumOwxx"
// CHECK: call {{.*}} @[[COMBO_ENUM_OUTLINED_DESTROY]]

Expand Down