Make C++ types impl Core.Default. (#6962)

C++ classes that are default-constructible now implement `Core.Default`
by calling the default constructor.
This commit is contained in:
Richard Smith
2026-03-25 21:07:24 +00:00
committed by GitHub
parent 311670c84a
commit 37b238fa28
49 changed files with 1069 additions and 426 deletions
@@ -21,6 +21,10 @@ class C {
C();
};
struct NoDefault {
NoDefault(int);
};
// --- import_default.carbon
library "[[@TEST_NAME]]";
@@ -33,6 +37,59 @@ fn F() {
//@dump-sem-ir-end
}
// --- implicit_default.carbon
library "[[@TEST_NAME]]";
import Cpp library "default.h";
fn F() {
//@dump-sem-ir-begin
var unused c: Cpp.C;
//@dump-sem-ir-end
}
fn G(T:! Core.Default & Core.Destroy) {
var unused x: T;
}
fn CallG() {
G(Cpp.C);
}
// --- todo_fail_no_default_decl.carbon
library "[[@TEST_NAME]]";
import Cpp library "default.h";
fn F() {
// TODO: This should fail, but currently does not because the min_prelude
// version of UnformedInit is currently implemented by every type.
var unused x: Cpp.NoDefault;
}
// --- fail_no_default.carbon
library "[[@TEST_NAME]]";
import Cpp library "default.h";
fn G(T:! Core.Default & Core.Destroy) {
var unused x: T;
}
fn CallG() {
// CHECK:STDERR: fail_no_default.carbon:[[@LINE+7]]:3: error: cannot convert type `NoDefault` into type implementing `Core.Default & Core.Destroy` [ConversionFailureTypeToFacet]
// CHECK:STDERR: G(Cpp.NoDefault);
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_no_default.carbon:[[@LINE-8]]:6: note: initializing generic parameter `T` declared here [InitializingGenericParam]
// CHECK:STDERR: fn G(T:! Core.Default & Core.Destroy) {
// CHECK:STDERR: ^
// CHECK:STDERR:
G(Cpp.NoDefault);
}
// ============================================================================
// Non default constructor
// ============================================================================
@@ -308,6 +365,62 @@ fn F() {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- implicit_default.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %pattern_type.217: type = pattern_type %C [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete]
// CHECK:STDOUT: %Default.type: type = facet_type <@Default> [concrete]
// CHECK:STDOUT: %T.50c: %Default.type = symbolic_binding T, 0 [symbolic]
// CHECK:STDOUT: %T.binding.as_type.as.DefaultOrUnformed.impl.Op.type.8d1: type = fn_type @T.binding.as_type.as.DefaultOrUnformed.impl.Op, @T.binding.as_type.as.DefaultOrUnformed.impl(%T.50c) [symbolic]
// CHECK:STDOUT: %T.binding.as_type.as.DefaultOrUnformed.impl.Op.185: %T.binding.as_type.as.DefaultOrUnformed.impl.Op.type.8d1 = struct_value () [symbolic]
// CHECK:STDOUT: %C.Op.type: type = fn_type @C.Op [concrete]
// CHECK:STDOUT: %C.Op: %C.Op.type = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.a0a: <witness> = custom_witness (%C.Op), @Default [concrete]
// CHECK:STDOUT: %Default.facet.fdc: %Default.type = facet_value %C, (%custom_witness.a0a) [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness.2aa: <witness> = impl_witness imports.%DefaultOrUnformed.impl_witness_table.608, @T.binding.as_type.as.DefaultOrUnformed.impl(%Default.facet.fdc) [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.facet.898: %DefaultOrUnformed.type = facet_value %C, (%DefaultOrUnformed.impl_witness.2aa) [concrete]
// CHECK:STDOUT: %C.cpp_destructor.type: type = fn_type @C.cpp_destructor [concrete]
// CHECK:STDOUT: %C.cpp_destructor: %C.cpp_destructor.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: %Core.import_ref.9c6: @T.binding.as_type.as.DefaultOrUnformed.impl.%T.binding.as_type.as.DefaultOrUnformed.impl.Op.type (%T.binding.as_type.as.DefaultOrUnformed.impl.Op.type.8d1) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.binding.as_type.as.DefaultOrUnformed.impl.%T.binding.as_type.as.DefaultOrUnformed.impl.Op (constants.%T.binding.as_type.as.DefaultOrUnformed.impl.Op.185)]
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table.608 = impl_witness_table (%Core.import_ref.9c6), @T.binding.as_type.as.DefaultOrUnformed.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %c.patt: %pattern_type.217 = ref_binding_pattern c [concrete]
// CHECK:STDOUT: %c.var_patt: %pattern_type.217 = var_pattern %c.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %c.var: ref %C = var %c.var_patt
// CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%C, (constants.%DefaultOrUnformed.impl_witness.2aa) [concrete = constants.%DefaultOrUnformed.facet.898]
// CHECK:STDOUT: %.loc8_22.1: %DefaultOrUnformed.type = converted constants.%C, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet.898]
// CHECK:STDOUT: %as_type: type = facet_access_type %.loc8_22.1 [concrete = constants.%C]
// CHECK:STDOUT: %.loc8_22.2: type = converted %.loc8_22.1, %as_type [concrete = constants.%C]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %.loc8_3: ref %C = splice_block %c.var {}
// CHECK:STDOUT: %T.binding.as_type.as.DefaultOrUnformed.impl.Op.call: init %C to %.loc8_3 = call %T.binding.as_type.as.DefaultOrUnformed.impl.Op.specific_fn()
// CHECK:STDOUT: assign %c.var, %T.binding.as_type.as.DefaultOrUnformed.impl.Op.call
// CHECK:STDOUT: %.loc8_20: type = splice_block %C.ref [concrete = constants.%C] {
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: }
// CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var
// CHECK:STDOUT: %C.cpp_destructor.bound: <bound method> = bound_method %c.var, constants.%C.cpp_destructor
// CHECK:STDOUT: %C.cpp_destructor.call: init %empty_tuple.type = call %C.cpp_destructor.bound(%c.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_non_default.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {