mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 15:10:12 +01:00
Fix crash attempting constant evaluation of a constexpr constructor. (#7514)
We can't use a `CallExpr` to call a constructor; use a `CXXConstructExpr` instead. While this fixes the crash and gets us past the initial constant evaluation, we still can't map the constant value back into Carbon, so this doesn't actually make constexpr constructors work yet. But it does stop Clang from crashing. Fixes #7498.
This commit is contained in:
@@ -226,22 +226,7 @@ auto EvalCppCall(Context& context, SemIR::LocId loc_id,
|
||||
const auto& args = context.inst_blocks().Get(args_id);
|
||||
|
||||
auto* function_decl = cast<clang::FunctionDecl>(clang_decl.decl());
|
||||
|
||||
// Create expr for the function declaration.
|
||||
auto* decl_ref_expr = clang::DeclRefExpr::Create(
|
||||
context.ast_context(), /*QualifierLoc=*/clang::NestedNameSpecifierLoc(),
|
||||
/*TemplateKWLoc=*/clang::SourceLocation(), function_decl,
|
||||
/*RefersToEnclosingVariableOrCapture=*/false,
|
||||
/*NameLoc=*/GetCppLocation(context, loc_id), function_decl->getType(),
|
||||
clang::VK_LValue);
|
||||
|
||||
// Cast to a function pointer type.
|
||||
auto function_ptr_type =
|
||||
context.ast_context().getPointerType(function_decl->getType());
|
||||
auto* implicit_cast_expr = clang::ImplicitCastExpr::Create(
|
||||
context.ast_context(), function_ptr_type,
|
||||
clang::CK_FunctionToPointerDecay, decl_ref_expr, nullptr,
|
||||
clang::VK_PRValue, clang::FPOptionsOverride());
|
||||
auto loc = GetCppLocation(context, loc_id);
|
||||
|
||||
// Convert the arguments to exprs.
|
||||
clang::SmallVector<clang::Expr*> arg_exprs;
|
||||
@@ -256,11 +241,36 @@ auto EvalCppCall(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
// Create an expr to call the function.
|
||||
auto* call_expr = clang::CallExpr::Create(
|
||||
context.ast_context(), implicit_cast_expr, arg_exprs,
|
||||
function_decl->getCallResultType(), clang::VK_PRValue,
|
||||
/*RParenLoc=*/GetCppLocation(context, loc_id),
|
||||
clang::FPOptionsOverride());
|
||||
clang::Expr* call_expr;
|
||||
if (auto* ctor = dyn_cast<clang::CXXConstructorDecl>(function_decl)) {
|
||||
// Constructor: generate a direct constructor call expression.
|
||||
call_expr = clang::CXXConstructExpr::Create(
|
||||
context.ast_context(), function_decl->getCallResultType(), loc, ctor,
|
||||
/*Elidable=*/false, arg_exprs, /*HadMultipleCandidates=*/false,
|
||||
/*ListInitialization=*/false, /*StdInitListInitialization=*/false,
|
||||
/*ZeroInitialization=*/false, clang::CXXConstructionKind::Complete,
|
||||
clang::SourceRange(loc));
|
||||
} else {
|
||||
// Create expr for the function declaration.
|
||||
auto* decl_ref_expr = clang::DeclRefExpr::Create(
|
||||
context.ast_context(), /*QualifierLoc=*/clang::NestedNameSpecifierLoc(),
|
||||
/*TemplateKWLoc=*/clang::SourceLocation(), function_decl,
|
||||
/*RefersToEnclosingVariableOrCapture=*/false,
|
||||
/*NameLoc=*/loc, function_decl->getType(), clang::VK_LValue);
|
||||
|
||||
// Cast to a function pointer type.
|
||||
// TODO: For a non-static member function, we should create a member access.
|
||||
auto function_ptr_type =
|
||||
context.ast_context().getPointerType(function_decl->getType());
|
||||
auto* implicit_cast_expr = clang::ImplicitCastExpr::Create(
|
||||
context.ast_context(), function_ptr_type,
|
||||
clang::CK_FunctionToPointerDecay, decl_ref_expr, nullptr,
|
||||
clang::VK_PRValue, clang::FPOptionsOverride());
|
||||
call_expr = clang::CallExpr::Create(
|
||||
context.ast_context(), implicit_cast_expr, arg_exprs,
|
||||
function_decl->getCallResultType(), clang::VK_PRValue,
|
||||
/*RParenLoc=*/loc, clang::FPOptionsOverride());
|
||||
}
|
||||
|
||||
// Evaluate the expr as a constant and map that to Carbon constant.
|
||||
clang::SmallVector<clang::PartialDiagnosticAt> notes;
|
||||
|
||||
@@ -127,3 +127,148 @@ consteval int f(const int &r) { return r; }
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
var b: i32 = Cpp.f(a);
|
||||
|
||||
// --- fail_todo_constructor.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp;
|
||||
|
||||
inline Cpp '''
|
||||
struct C {
|
||||
constexpr C(int a) : a(a) {}
|
||||
int a;
|
||||
};
|
||||
''';
|
||||
|
||||
// TODO: This should probably be allowed.
|
||||
eval fn F() -> i32 {
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: fail_todo_constructor.carbon:[[@LINE+3]]:18: error: expression is runtime; expected constant [EvalRequiresConstantValue]
|
||||
// CHECK:STDERR: let c: Cpp.C = 3;
|
||||
// CHECK:STDERR: ^
|
||||
let c: Cpp.C = 3;
|
||||
return c.a;
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_todo_constructor.carbon:[[@LINE+4]]:19: note: in call to F here [InCallToEvalFn]
|
||||
// CHECK:STDERR: let a: array(i32, F()) = (1, 2, 3);
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
let a: array(i32, F()) = (1, 2, 3);
|
||||
|
||||
// CHECK:STDOUT: --- fail_todo_constructor.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.bbc: type = pattern_type %C [concrete]
|
||||
// CHECK:STDOUT: %c.patt: %pattern_type.bbc = value_binding_pattern c [concrete]
|
||||
// CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
|
||||
// CHECK:STDOUT: %ptr.0a2: type = ptr_type %C [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.914: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.845: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.a23: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.b3c, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl.0e9(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.2ba = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet.9f1: %ImplicitAs.type.914 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.a23) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.740: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.9f1) [concrete]
|
||||
// CHECK:STDOUT: %.1c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.740, %ImplicitAs.facet.9f1 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_3.410: %i32 = int_value 3 [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.ac8: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.5e0: %Int.as.Copy.impl.Op.type.ac8 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %Copy.impl_witness.0e0: <witness> = impl_witness imports.%Copy.impl_witness_table.367, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.3f6: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.4f6: %Int.as.Copy.impl.Op.type.3f6 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.0e0) [concrete]
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.type.d09: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
|
||||
// CHECK:STDOUT: %.7a6: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d09, %Copy.facet [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.4f6, @Int.as.Copy.impl.Op(%int_32) [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: %C.Op.type: type = fn_type @C.Op [concrete]
|
||||
// CHECK:STDOUT: %C.Op: %C.Op.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: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import_ref.edf: @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.845)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.b3c = impl_witness_table (%Core.import_ref.edf), @Core.IntLiteral.as.ImplicitAs.impl.0e9 [concrete]
|
||||
// CHECK:STDOUT: %Core.import_ref.cd6: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.ac8) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.5e0)]
|
||||
// CHECK:STDOUT: %Copy.impl_witness_table.367 = impl_witness_table (%Core.import_ref.cd6), @Int.as.Copy.impl [concrete]
|
||||
// CHECK:STDOUT: %C.cpp_destructor.decl: %C.cpp_destructor.type = fn_decl @C.cpp_destructor [concrete = constants.%C.cpp_destructor] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
|
||||
// CHECK:STDOUT: %.loc19_18.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %impl.elem0.loc19: %.1c5 = impl_witness_access constants.%ImplicitAs.impl_witness.a23, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e39]
|
||||
// CHECK:STDOUT: %bound_method.loc19_18.1: <bound method> = bound_method %int_3, %impl.elem0.loc19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn.loc19: <specific function> = specific_function %impl.elem0.loc19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc19_18.2: <bound method> = bound_method %int_3, %specific_fn.loc19 [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc19_18.2(%int_3) [concrete = constants.%int_3.410]
|
||||
// CHECK:STDOUT: %.loc19_18.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.410]
|
||||
// CHECK:STDOUT: %.loc19_18.3: %i32 = converted %int_3, %.loc19_18.2 [concrete = constants.%int_3.410]
|
||||
// CHECK:STDOUT: %addr: %ptr.0a2 = addr_of %.loc19_18.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc19_18.3, %addr)
|
||||
// CHECK:STDOUT: %.loc19_18.4: init %C to %.loc19_18.1 = mark_in_place_init %C__carbon_thunk.call
|
||||
// CHECK:STDOUT: %.loc19_18.5: init %C = converted %int_3, %.loc19_18.4
|
||||
// CHECK:STDOUT: %.loc19_18.6: ref %C = temporary %.loc19_18.1, %.loc19_18.5
|
||||
// CHECK:STDOUT: %.loc19_18.7: %C = acquire_value %.loc19_18.6
|
||||
// CHECK:STDOUT: %.loc19_13: 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: %C = wrapper_binding c, %.loc19_18.7
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c.patt: %pattern_type.bbc = value_binding_pattern c [concrete = constants.%c.patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c.ref: %C = name_ref c, %c
|
||||
// CHECK:STDOUT: %a.ref: %C.elem = name_ref a, @C.%.1 [concrete = @C.%.1]
|
||||
// CHECK:STDOUT: %.loc20_11.1: ref %i32 = class_element_access %c.ref, element0
|
||||
// CHECK:STDOUT: %.loc20_11.2: %i32 = acquire_value %.loc20_11.1
|
||||
// CHECK:STDOUT: %impl.elem0.loc20: %.7a6 = impl_witness_access constants.%Copy.impl_witness.0e0, element0 [concrete = constants.%Int.as.Copy.impl.Op.4f6]
|
||||
// CHECK:STDOUT: %bound_method.loc20_11.1: <bound method> = bound_method %.loc20_11.2, %impl.elem0.loc20
|
||||
// CHECK:STDOUT: %specific_fn.loc20: <specific function> = specific_function %impl.elem0.loc20, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc20_11.2: <bound method> = bound_method %.loc20_11.2, %specific_fn.loc20
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc20_11.2(%.loc20_11.2)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %C.Op.bound: <bound method> = bound_method %.loc19_18.6, constants.%C.Op
|
||||
// CHECK:STDOUT: %Op.ref: %C.cpp_destructor.type = name_ref Op, imports.%C.cpp_destructor.decl [concrete = constants.%C.cpp_destructor]
|
||||
// CHECK:STDOUT: %C.cpp_destructor.bound: <bound method> = bound_method %.loc19_18.6, %Op.ref
|
||||
// CHECK:STDOUT: %C.cpp_destructor.call: init %empty_tuple.type = call %C.cpp_destructor.bound(%.loc19_18.6)
|
||||
// CHECK:STDOUT: return %Int.as.Copy.impl.Op.call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
Reference in New Issue
Block a user