Skip to content

Commit 1ec8ac7

Browse files
zygoloidgeoffromer
andauthored
Add Copy interface and use it for making copies. (#6034)
Instead of hardcoding which types are copyable, add a `Core.Copy` interface to perform copying. Move almost all the current copy support to that interface. Some remaining pieces are still using builtin logic after this PR: * For tuples and structs, builtin logic is used to perform elementwise copies. This also supports copying *adapters of* tuples and structs, which seems like it may not be desirable, especially for non-extending adapters. A `Copy` impl is provided for tuples of at most 2 elements, so that `Core.Copy` constraints are satisfied, but we can't implement this generally until we have variadics support, and don't yet have a mechanism to generalize this to structs. * For `enum` types imported from C++, builtin logic is used to perform a copy. This is temporary until we have a mechanism to identify these types from an impl in the prelude. One lowering test in `toolchain/lower/testdata/class/generic.carbon` is disabled for now, as it causes a crash in the lowering code due to an ABI mismatch between the call signature in the lowered declaration of a specific function and the call that is generated in the specific callee. Fixing this is a little involved, and will be done in a separate PR. --------- Co-authored-by: Geoff Romer <[email protected]>
1 parent 91722ae commit 1ec8ac7

File tree

207 files changed

+6196
-2567
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

207 files changed

+6196
-2567
lines changed

core/prelude.carbon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package Core library "prelude";
88

9+
export import library "prelude/copy";
910
export import library "prelude/destroy";
1011
export import library "prelude/iterate";
1112
export import library "prelude/operators";

core/prelude/copy.carbon

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
package Core library "prelude/copy";
6+
7+
// Copying an object, which is a conversion from a value representation to an
8+
// initializing representation.
9+
interface Copy {
10+
fn Op[self: Self]() -> Self;
11+
}
12+
13+
private fn Bool() -> type = "bool.make_type";
14+
private fn CharLiteral() -> type = "char_literal.make_type";
15+
private fn FloatLiteral() -> type = "float_literal.make_type";
16+
private fn IntLiteral() -> type = "int_literal.make_type";
17+
18+
impl Bool() as Copy {
19+
fn Op[self: Self]() -> Self = "primitive_copy";
20+
}
21+
22+
impl CharLiteral() as Copy {
23+
fn Op[self: Self]() -> Self = "primitive_copy";
24+
}
25+
26+
impl FloatLiteral() as Copy {
27+
fn Op[self: Self]() -> Self = "primitive_copy";
28+
}
29+
30+
impl IntLiteral() as Copy {
31+
fn Op[self: Self]() -> Self = "primitive_copy";
32+
}
33+
34+
impl type as Copy {
35+
fn Op[self: Self]() -> Self = "primitive_copy";
36+
}
37+
38+
impl forall [T:! type] T* as Copy {
39+
fn Op[self: Self]() -> Self = "primitive_copy";
40+
}
41+
42+
// TODO: Implement tuple copy as a variadic generic impl.
43+
impl () as Copy {
44+
fn Op[self: Self]() -> Self = "no_op";
45+
}
46+
47+
impl forall [T:! Copy] (T,) as Copy {
48+
fn Op[self: Self]() -> Self {
49+
return (self.0.Op(),);
50+
}
51+
}
52+
53+
impl forall [T:! Copy, U:! Copy] (T, U) as Copy {
54+
fn Op[self: Self]() -> Self {
55+
return (self.0.Op(), self.1.Op());
56+
}
57+
}

core/prelude/iterate.carbon

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@
44

55
package Core library "prelude/iterate";
66

7+
export import library "prelude/copy";
78
export import library "prelude/destroy";
89
export import library "prelude/types";
910
export import library "prelude/operators";
1011

1112
interface Iterate {
12-
let ElementType:! type;
13+
// TODO: Support iterating ranges of non-copyable values.
14+
let ElementType:! Copy;
1315
let CursorType:! type;
1416
fn NewCursor[self: Self]() -> CursorType;
1517
fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType);
1618
}
1719

18-
impl forall [T:! type, N:! IntLiteral()]
20+
impl forall [T:! Copy, N:! IntLiteral()]
1921
array(T, N) as Iterate
2022
where .ElementType = T and .CursorType = i32 {
2123
fn NewCursor[self: Self]() -> i32 { return 0; }

core/prelude/types/char.carbon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/types/char";
66

7+
import library "prelude/copy";
78
import library "prelude/destroy";
89
import library "prelude/operators";
910
import library "prelude/types/uint";
@@ -14,6 +15,10 @@ class Char {
1415
adapt u8;
1516
}
1617

18+
impl Char as Copy {
19+
fn Op[self: Self]() -> Self = "primitive_copy";
20+
}
21+
1722
impl CharLiteral() as ImplicitAs(Char) {
1823
fn Convert[self: Self]() -> Char = "char.convert_checked";
1924
}

core/prelude/types/float.carbon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/types/float";
66

7+
import library "prelude/copy";
78
import library "prelude/destroy";
89
import library "prelude/operators";
910
import library "prelude/types/float_literal";
@@ -15,6 +16,12 @@ class Float(N:! IntLiteral()) {
1516
adapt MakeFloat(N);
1617
}
1718

19+
// Copy.
20+
21+
impl forall [N:! IntLiteral()] Float(N) as Copy {
22+
fn Op[self: Self]() -> Self = "primitive_copy";
23+
}
24+
1825
// Conversions.
1926
// TODO: Support mixed-size conversions.
2027
// TODO: Support int-to-float conversions.

core/prelude/types/int.carbon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/types/int";
66

7+
import library "prelude/copy";
78
import library "prelude/destroy";
89
import library "prelude/operators";
910
import library "prelude/types/int_literal";
@@ -14,6 +15,12 @@ class Int(N:! IntLiteral()) {
1415
adapt MakeInt(N);
1516
}
1617

18+
// Copy.
19+
20+
impl forall [N:! IntLiteral()] Int(N) as Copy {
21+
fn Op[self: Self]() -> Self = "primitive_copy";
22+
}
23+
1724
// Conversions.
1825

1926
impl forall [To:! IntLiteral()] IntLiteral() as ImplicitAs(Int(To)) {

core/prelude/types/optional.carbon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/types/optional";
66

7+
import library "prelude/copy";
78
import library "prelude/destroy";
89
import library "prelude/types/bool";
910

@@ -15,7 +16,7 @@ import library "prelude/types/bool";
1516
//
1617
// TODO: We don't have an approved design for an `Optional` type yet, but it's
1718
// used by the design for `Iterate`. The API here is a placeholder.
18-
class Optional(T:! type) {
19+
class Optional(T:! Copy) {
1920
fn None() -> Self {
2021
returned var me: Self;
2122
me.has_value = false;

core/prelude/types/uint.carbon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/types/uint";
66

7+
import library "prelude/copy";
78
import library "prelude/destroy";
89
import library "prelude/operators";
910
import library "prelude/types/int";
@@ -15,6 +16,12 @@ class UInt(N:! IntLiteral()) {
1516
adapt MakeUInt(N);
1617
}
1718

19+
// Copy.
20+
21+
impl forall [N:! IntLiteral()] UInt(N) as Copy {
22+
fn Op[self: Self]() -> Self = "primitive_copy";
23+
}
24+
1825
// Conversions.
1926

2027
impl forall [To:! IntLiteral()] IntLiteral() as ImplicitAs(UInt(To)) {

toolchain/check/convert.cpp

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,30 +1266,40 @@ static auto PerformBuiltinConversion(
12661266
return value_id;
12671267
}
12681268

1269+
// Determine whether this is a C++ enum type.
1270+
// TODO: This should be removed once we can properly add a `Copy` impl for C++
1271+
// enum types.
1272+
static auto IsCppEnum(Context& context, SemIR::TypeId type_id) -> bool {
1273+
auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id);
1274+
if (!class_type) {
1275+
return false;
1276+
}
1277+
1278+
// A C++-imported class type that is an adapter is an enum.
1279+
auto& class_info = context.classes().Get(class_type->class_id);
1280+
return class_info.adapt_id.has_value() &&
1281+
context.name_scopes().Get(class_info.scope_id).is_cpp_scope();
1282+
}
1283+
12691284
// Given a value expression, form a corresponding initializer that copies from
12701285
// that value, if it is possible to do so.
12711286
static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
12721287
-> SemIR::InstId {
1273-
auto expr = context.insts().Get(expr_id);
1274-
auto type_id = expr.type_id();
1275-
if (type_id == SemIR::ErrorInst::TypeId) {
1276-
return SemIR::ErrorInst::InstId;
1288+
// TODO: We don't have a mechanism yet to generate `Copy` impls for each enum
1289+
// type imported from C++. For now we fake it by providing a direct copy.
1290+
if (IsCppEnum(context, context.insts().Get(expr_id).type_id())) {
1291+
return CopyValueToTemporary(context, expr_id);
12771292
}
12781293

1279-
if (InitReprIsCopyOfValueRepr(context.sem_ir(), type_id)) {
1280-
// For simple by-value types, no explicit action is required. Initializing
1281-
// from a value expression is treated as copying the value.
1282-
return expr_id;
1283-
}
1284-
1285-
// TODO: We don't yet have rules for whether and when a class type is
1286-
// copyable, or how to perform the copy.
1287-
if (diagnose) {
1288-
CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error,
1289-
"cannot copy value of type {0}", TypeOfInstId);
1290-
context.emitter().Emit(expr_id, CopyOfUncopyableType, expr_id);
1291-
}
1292-
return SemIR::ErrorInst::InstId;
1294+
return BuildUnaryOperator(
1295+
context, SemIR::LocId(expr_id), {"Copy"}, expr_id, [&] {
1296+
if (!diagnose) {
1297+
return context.emitter().BuildSuppressed();
1298+
}
1299+
CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error,
1300+
"cannot copy value of type {0}", TypeOfInstId);
1301+
return context.emitter().Build(expr_id, CopyOfUncopyableType, expr_id);
1302+
});
12931303
}
12941304

12951305
// Convert a value expression so that it can be used to initialize a C++ thunk
@@ -1310,14 +1320,7 @@ static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id,
13101320
// Otherwise, we need a temporary to pass as the thunk argument. Create a copy
13111321
// and initialize a temporary from it.
13121322
expr_id = PerformCopy(context, expr_id, diagnose);
1313-
if (SemIR::GetExprCategory(context.sem_ir(), expr_id) ==
1314-
SemIR::ExprCategory::Value) {
1315-
// If we still have a value expression, then it's a value expression
1316-
// whose value is being used directly to initialize the object. Copy
1317-
// it into a temporary to form an ephemeral reference.
1318-
expr_id = CopyValueToTemporary(context, expr_id);
1319-
}
1320-
return expr_id;
1323+
return MaterializeIfInitializing(context, expr_id);
13211324
}
13221325

13231326
// Returns the Core interface name to use for a given kind of conversion.

toolchain/check/eval.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,10 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
16561656
phase);
16571657
}
16581658

1659+
case SemIR::BuiltinFunctionKind::PrimitiveCopy: {
1660+
return context.constant_values().Get(arg_ids[0]);
1661+
}
1662+
16591663
case SemIR::BuiltinFunctionKind::PrintChar:
16601664
case SemIR::BuiltinFunctionKind::PrintInt:
16611665
case SemIR::BuiltinFunctionKind::ReadChar:

0 commit comments

Comments
 (0)