mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Although this focused on `Destroy` support, some choices here around `implicit_type_impls` are because copy/move will likely follow a similar approach. I'm trying not to predict too much about how we'll structure those, but I'm putting `Destroy` impl logic in a file that could perhaps be shared with those. They'd likely be interested in similar things, e.g. traversing members of types (particularly class, struct literal, tuple literal). At present this sets the destroy function as `no_op` which is consistent with current logic, but has a TODO to correctly define. Constant importing for functions changes slightly due to some issues I was having with `GetFunctionType`. zygoloid suggested this approach to avoid `EvalInst` logic. Adds a flag for controlling whether to generating these impls. While this does generation for `class`, as noted above this'll also need to be done for tuples and struct literals, which would leave the `none.carbon` min_prelude unable to use any types. Note if destruction *would* occur, it'll still look up `Core.Destroy` for that and fail, but that's already true of any test using `none.carbon`. I'm trying to use the flag to see if we can keep `none.carbon` working mostly-consistently. I'd tried separating out the flag to #5852, but that got a lot of pushback over whether the behavior was appropriate. I'm hoping that the interactions here make it clearer why the particular approach -- the goal is not to enable advanced testing, or create some new end-user behavior that we really support, it's just to keep no-prelude tests functional. The main question raised there was why not just keep generating `impl T as Core.Destroy` if `fn destroy` is present -- but I think here it should be apparent that would require additional complexity, as the generation of `impl T as Core.Destroy` is not currently conditioned based on the implementation of `fn destroy`. I'd rather add complexity to this flag only if it's enabling interesting test functionality. --------- Co-authored-by: Geoff Romer <gromer@google.com>
166 lines
6.6 KiB
C++
166 lines
6.6 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/check/pattern.h"
|
|
|
|
#include "toolchain/check/control_flow.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/return.h"
|
|
#include "toolchain/check/type.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
auto BeginSubpattern(Context& context) -> void {
|
|
context.inst_block_stack().Push();
|
|
context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd());
|
|
}
|
|
|
|
auto EndSubpatternAsExpr(Context& context, SemIR::InstId result_id)
|
|
-> SemIR::ExprRegionId {
|
|
if (context.region_stack().PeekRegion().size() > 1) {
|
|
// End the exit block with a branch to a successor block, whose contents
|
|
// will be determined later.
|
|
AddInst(context,
|
|
SemIR::LocIdAndInst::NoLoc<SemIR::Branch>(
|
|
{.target_id = context.inst_blocks().AddPlaceholder()}));
|
|
} else {
|
|
// This single-block region will be inserted as a SpliceBlock, so we don't
|
|
// need control flow out of it.
|
|
}
|
|
auto block_id = context.inst_block_stack().Pop();
|
|
CARBON_CHECK(block_id == context.region_stack().PeekRegion().back());
|
|
|
|
// TODO: Is it possible to validate that this region is genuinely
|
|
// single-entry, single-exit?
|
|
return context.sem_ir().expr_regions().Add(
|
|
{.block_ids = context.region_stack().PopRegion(),
|
|
.result_id = result_id});
|
|
}
|
|
|
|
auto EndSubpatternAsNonExpr(Context& context) -> void {
|
|
auto block_id = context.inst_block_stack().Pop();
|
|
CARBON_CHECK(block_id == context.region_stack().PeekRegion().back());
|
|
CARBON_CHECK(context.region_stack().PeekRegion().size() == 1);
|
|
CARBON_CHECK(context.inst_blocks().Get(block_id).empty());
|
|
context.region_stack().PopAndDiscardRegion();
|
|
}
|
|
|
|
auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
|
|
SemIR::NameId name_id, SemIR::TypeId type_id,
|
|
SemIR::ExprRegionId type_region_id, bool is_generic,
|
|
bool is_template) -> BindingPatternInfo {
|
|
auto entity_name_id = context.entity_names().AddSymbolicBindingName(
|
|
name_id, context.scope_stack().PeekNameScopeId(),
|
|
is_generic ? context.scope_stack().AddCompileTimeBinding()
|
|
: SemIR::CompileTimeBindIndex::None,
|
|
is_template);
|
|
|
|
auto bind_id = SemIR::InstId::None;
|
|
if (is_generic) {
|
|
bind_id = AddInstInNoBlock<SemIR::BindSymbolicName>(
|
|
context, name_loc,
|
|
{.type_id = type_id,
|
|
.entity_name_id = entity_name_id,
|
|
.value_id = SemIR::InstId::None});
|
|
} else {
|
|
bind_id =
|
|
AddInstInNoBlock<SemIR::BindName>(context, name_loc,
|
|
{.type_id = type_id,
|
|
.entity_name_id = entity_name_id,
|
|
.value_id = SemIR::InstId::None});
|
|
}
|
|
|
|
auto pattern_type_id = GetPatternType(context, type_id);
|
|
auto binding_pattern_id = SemIR::InstId::None;
|
|
if (is_generic) {
|
|
binding_pattern_id = AddPatternInst<SemIR::SymbolicBindingPattern>(
|
|
context, name_loc,
|
|
{.type_id = pattern_type_id, .entity_name_id = entity_name_id});
|
|
} else {
|
|
binding_pattern_id = AddPatternInst<SemIR::BindingPattern>(
|
|
context, name_loc,
|
|
{.type_id = pattern_type_id, .entity_name_id = entity_name_id});
|
|
}
|
|
|
|
if (is_generic) {
|
|
context.scope_stack().PushCompileTimeBinding(bind_id);
|
|
}
|
|
|
|
bool inserted =
|
|
context.bind_name_map()
|
|
.Insert(binding_pattern_id, {.bind_name_id = bind_id,
|
|
.type_expr_region_id = type_region_id})
|
|
.is_inserted();
|
|
CARBON_CHECK(inserted);
|
|
return {.pattern_id = binding_pattern_id, .bind_id = bind_id};
|
|
}
|
|
|
|
// Returns a VarStorage inst for the given `var` pattern. If the pattern
|
|
// is the body of a returned var, this reuses the return slot, and otherwise it
|
|
// adds a new inst.
|
|
static auto GetOrAddVarStorage(Context& context, SemIR::InstId var_pattern_id,
|
|
bool is_returned_var) -> SemIR::InstId {
|
|
if (is_returned_var) {
|
|
auto& function = GetCurrentFunctionForReturn(context);
|
|
auto return_info =
|
|
SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function);
|
|
if (return_info.has_return_slot()) {
|
|
return GetCurrentReturnSlot(context);
|
|
}
|
|
}
|
|
auto pattern = context.insts().GetWithLocId(var_pattern_id);
|
|
|
|
return AddInstWithCleanup(
|
|
context, pattern.loc_id,
|
|
SemIR::VarStorage{.type_id = ExtractScrutineeType(context.sem_ir(),
|
|
pattern.inst.type_id()),
|
|
.pattern_id = var_pattern_id});
|
|
}
|
|
|
|
auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id,
|
|
bool is_returned_var) -> void {
|
|
// We need to emit the VarStorage insts early, because they may be output
|
|
// arguments for the initializer. However, we can't emit them when we emit
|
|
// the corresponding `VarPattern`s because they're part of the pattern match,
|
|
// not part of the pattern.
|
|
// TODO: Find a way to do this without walking the whole pattern block.
|
|
for (auto inst_id : context.inst_blocks().Get(pattern_block_id)) {
|
|
if (context.insts().Is<SemIR::VarPattern>(inst_id)) {
|
|
context.var_storage_map().Insert(
|
|
inst_id, GetOrAddVarStorage(context, inst_id, is_returned_var));
|
|
}
|
|
}
|
|
}
|
|
|
|
auto AddSelfParamPattern(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ExprRegionId type_expr_region_id,
|
|
SemIR::TypeId type_id) -> SemIR::InstId {
|
|
SemIR::InstId pattern_id =
|
|
AddBindingPattern(context, loc_id, SemIR::NameId::SelfValue, type_id,
|
|
type_expr_region_id, /*is_generic=*/false,
|
|
/*is_template=*/false)
|
|
.pattern_id;
|
|
|
|
pattern_id = AddPatternInst<SemIR::ValueParamPattern>(
|
|
context, loc_id,
|
|
{.type_id = context.insts().Get(pattern_id).type_id(),
|
|
.subpattern_id = pattern_id,
|
|
.index = SemIR::CallParamIndex::None});
|
|
|
|
return pattern_id;
|
|
}
|
|
|
|
auto AddAddrSelfParamPattern(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ExprRegionId type_expr_region_id,
|
|
SemIR::TypeInstId type_inst_id) -> SemIR::InstId {
|
|
auto pattern_id = AddSelfParamPattern(context, loc_id, type_expr_region_id,
|
|
GetPointerType(context, type_inst_id));
|
|
return AddPatternInst<SemIR::AddrPattern>(
|
|
context, loc_id,
|
|
{.type_id = GetPatternType(context, SemIR::AutoType::TypeId),
|
|
.inner_id = pattern_id});
|
|
}
|
|
|
|
} // namespace Carbon::Check
|