mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Implement support for copying C++ classes. (#6434)
When performing impl lookup for `Core.Copy` for a C++ class type, look for a copy constructor. If we find one, synthesize an impl witness that calls the constructor. This adds initial support for impl lookup to delegate to the C++ interop logic for queries involving C++ types. For now, we don't implement the rules from #6166 that compare a synthesized type structure for the C++ impl against the best Carbon type structure, but the framework for building that support is established here. Currently there is no caching of the lookup here, and we build unique `ImplWitnessTable`s for each lookup, which leads to each impl lookup producing a distinct facet value. This results in some errors in generic contexts; this will be addressed in follow-up changes. This PR aims only to support the non-generic case. --------- Co-authored-by: Dana Jansens <danakj@orodu.net> Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
co-authored by
Dana Jansens
Carbon Infra Bot
parent
19660ccde0
commit
372f632d9d
+83
-66
@@ -9,6 +9,7 @@
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/check/call.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/cpp/operators.h"
|
||||
#include "toolchain/check/deferred_definition_worklist.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/check/function.h"
|
||||
@@ -213,67 +214,6 @@ static auto HasDeclaredReturnType(Context& context,
|
||||
.return_slot_pattern_id.has_value();
|
||||
}
|
||||
|
||||
auto BuildThunk(Context& context, SemIR::FunctionId signature_id,
|
||||
SemIR::SpecificId signature_specific_id,
|
||||
SemIR::InstId callee_id) -> SemIR::InstId {
|
||||
auto callee = SemIR::GetCalleeAsFunction(context.sem_ir(), callee_id);
|
||||
|
||||
// Check whether we can use the given function without a thunk.
|
||||
// TODO: For virtual functions, we want different rules for checking `self`.
|
||||
// TODO: This is too strict; for example, we should not compare parameter
|
||||
// names here.
|
||||
if (CheckFunctionTypeMatches(
|
||||
context, context.functions().Get(callee.function_id),
|
||||
context.functions().Get(signature_id), signature_specific_id,
|
||||
/*check_syntax=*/false, /*check_self=*/true, /*diagnose=*/false)) {
|
||||
return callee_id;
|
||||
}
|
||||
|
||||
// From P3763:
|
||||
// If the function in the interface does not have a return type, the
|
||||
// program is invalid if the function in the impl specifies a return type.
|
||||
//
|
||||
// Call into the redeclaration checking logic to produce a suitable error.
|
||||
//
|
||||
// TODO: Consider a different rule: always use an explicit return type for the
|
||||
// thunk, and always convert the result of the wrapped call to the return type
|
||||
// of the thunk.
|
||||
if (!HasDeclaredReturnType(context, signature_id) &&
|
||||
HasDeclaredReturnType(context, callee.function_id)) {
|
||||
bool success = CheckFunctionReturnTypeMatches(
|
||||
context, context.functions().Get(callee.function_id),
|
||||
context.functions().Get(signature_id), signature_specific_id);
|
||||
CARBON_CHECK(!success, "Return type unexpectedly matches");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
// Create a scope for the function's parameters and generic parameters.
|
||||
context.scope_stack().PushForDeclName();
|
||||
|
||||
// We can't use the function directly. Build a thunk.
|
||||
// TODO: Check for and diagnose obvious reasons why this will fail, such as
|
||||
// arity mismatch, before trying to build the thunk.
|
||||
auto [function_id, thunk_id] =
|
||||
CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id,
|
||||
signature_specific_id, callee.function_id);
|
||||
|
||||
// Track that this function is a thunk.
|
||||
context.functions().Get(function_id).SetThunk(callee_id);
|
||||
|
||||
// Register the thunk to be defined when we reach the end of the enclosing
|
||||
// deferred definition scope, for example an `impl` or `class` definition, as
|
||||
// if the thunk's body were written inline in this location.
|
||||
context.deferred_definition_worklist().SuspendThunkAndPush(
|
||||
context, {
|
||||
.signature_id = signature_id,
|
||||
.function_id = function_id,
|
||||
.decl_id = thunk_id,
|
||||
.callee_id = callee_id,
|
||||
});
|
||||
|
||||
return thunk_id;
|
||||
}
|
||||
|
||||
// Build an expression that names the value matched by a pattern.
|
||||
static auto BuildPatternRef(Context& context,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids,
|
||||
@@ -303,16 +243,25 @@ auto PerformThunkCall(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId callee_id) -> SemIR::InstId {
|
||||
auto& function = context.functions().Get(function_id);
|
||||
|
||||
llvm::SmallVector<SemIR::InstId> args;
|
||||
|
||||
// If we have a self parameter, form `self.<callee_id>`.
|
||||
if (function.self_param_id.has_value()) {
|
||||
callee_id = PerformCompoundMemberAccess(
|
||||
context, loc_id,
|
||||
BuildPatternRef(context, call_arg_ids, function.self_param_id),
|
||||
callee_id);
|
||||
auto self_arg_id =
|
||||
BuildPatternRef(context, call_arg_ids, function.self_param_id);
|
||||
if (IsCppConstructorOrNonMethodOperator(context, callee_id)) {
|
||||
// When calling a C++ constructor to implement `Copy`, or calling a C++
|
||||
// non-method operator to implement a Carbon operator, the interface has a
|
||||
// `self` parameter but C++ models that parameter as an explicit argument
|
||||
// instead, so add the `self` to the argument list instead in that case.
|
||||
args.push_back(self_arg_id);
|
||||
} else {
|
||||
callee_id =
|
||||
PerformCompoundMemberAccess(context, loc_id, self_arg_id, callee_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Form an argument list.
|
||||
llvm::SmallVector<SemIR::InstId> args;
|
||||
for (auto pattern_id :
|
||||
context.inst_blocks().Get(function.param_patterns_id)) {
|
||||
args.push_back(BuildPatternRef(context, call_arg_ids, pattern_id));
|
||||
@@ -423,4 +372,72 @@ auto BuildThunkDefinition(Context& context,
|
||||
context.scope_stack().Pop();
|
||||
}
|
||||
|
||||
auto BuildThunk(Context& context, SemIR::FunctionId signature_id,
|
||||
SemIR::SpecificId signature_specific_id,
|
||||
SemIR::InstId callee_id, bool defer_definition)
|
||||
-> SemIR::InstId {
|
||||
auto callee = SemIR::GetCalleeAsFunction(context.sem_ir(), callee_id);
|
||||
|
||||
// Check whether we can use the given function without a thunk.
|
||||
// TODO: For virtual functions, we want different rules for checking `self`.
|
||||
// TODO: This is too strict; for example, we should not compare parameter
|
||||
// names here.
|
||||
if (CheckFunctionTypeMatches(
|
||||
context, context.functions().Get(callee.function_id),
|
||||
context.functions().Get(signature_id), signature_specific_id,
|
||||
/*check_syntax=*/false, /*check_self=*/true, /*diagnose=*/false)) {
|
||||
return callee_id;
|
||||
}
|
||||
|
||||
// From P3763:
|
||||
// If the function in the interface does not have a return type, the
|
||||
// program is invalid if the function in the impl specifies a return type.
|
||||
//
|
||||
// Call into the redeclaration checking logic to produce a suitable error.
|
||||
//
|
||||
// TODO: Consider a different rule: always use an explicit return type for the
|
||||
// thunk, and always convert the result of the wrapped call to the return type
|
||||
// of the thunk.
|
||||
if (!HasDeclaredReturnType(context, signature_id) &&
|
||||
HasDeclaredReturnType(context, callee.function_id)) {
|
||||
bool success = CheckFunctionReturnTypeMatches(
|
||||
context, context.functions().Get(callee.function_id),
|
||||
context.functions().Get(signature_id), signature_specific_id);
|
||||
CARBON_CHECK(!success, "Return type unexpectedly matches");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
// Create a scope for the function's parameters and generic parameters.
|
||||
context.scope_stack().PushForDeclName();
|
||||
|
||||
// We can't use the function directly. Build a thunk.
|
||||
// TODO: Check for and diagnose obvious reasons why this will fail, such as
|
||||
// arity mismatch, before trying to build the thunk.
|
||||
auto [function_id, thunk_id] =
|
||||
CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id,
|
||||
signature_specific_id, callee.function_id);
|
||||
|
||||
// Track that this function is a thunk.
|
||||
context.functions().Get(function_id).SetThunk(callee_id);
|
||||
|
||||
if (defer_definition) {
|
||||
// Register the thunk to be defined when we reach the end of the enclosing
|
||||
// deferred definition scope, for example an `impl` or `class` definition,
|
||||
// as if the thunk's body were written inline in this location.
|
||||
context.deferred_definition_worklist().SuspendThunkAndPush(
|
||||
context, {
|
||||
.signature_id = signature_id,
|
||||
.function_id = function_id,
|
||||
.decl_id = thunk_id,
|
||||
.callee_id = callee_id,
|
||||
});
|
||||
} else {
|
||||
BuildThunkDefinition(context, signature_id, function_id, thunk_id,
|
||||
callee_id);
|
||||
context.scope_stack().Pop();
|
||||
}
|
||||
|
||||
return thunk_id;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
Reference in New Issue
Block a user