mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:54:41 +01:00
Check support for form literals and :? bindings (#6747)
Co-authored-by: Jon Ross-Perkins <jperkins@google.com> Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
co-authored by
Jon Ross-Perkins
Carbon Infra Bot
parent
f9ab963bd6
commit
bf9219d30e
@@ -256,7 +256,11 @@ class Context {
|
||||
}
|
||||
|
||||
// Data about a form expression.
|
||||
//
|
||||
// TODO: consider moving this out of Context.
|
||||
struct FormExpr {
|
||||
static const FormExpr Error;
|
||||
|
||||
// The inst ID of the form expression itself. This is always a form inst,
|
||||
// such as InitForm or RefForm.
|
||||
// TODO: Consider creating an AnyForm inst category to refer to those insts.
|
||||
@@ -532,6 +536,11 @@ class Context {
|
||||
CoreIdentifierCache core_identifiers_;
|
||||
};
|
||||
|
||||
inline constexpr Context::FormExpr Context::FormExpr::Error = {
|
||||
.form_inst_id = SemIR::ErrorInst::InstId,
|
||||
.type_component_inst_id = SemIR::ErrorInst::TypeInstId,
|
||||
.type_component_id = SemIR::ErrorInst::TypeId};
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_CONTEXT_H_
|
||||
|
||||
+41
-15
@@ -819,6 +819,7 @@ static auto IsValidExprCategoryForConversionTarget(
|
||||
return category == SemIR::ExprCategory::DurableRef;
|
||||
case ConversionTarget::CppThunkRef:
|
||||
return category == SemIR::ExprCategory::EphemeralRef;
|
||||
case ConversionTarget::NoOp:
|
||||
case ConversionTarget::ExplicitAs:
|
||||
case ConversionTarget::ExplicitUnsafeAs:
|
||||
return true;
|
||||
@@ -1721,6 +1722,11 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
if (target.kind == ConversionTarget::NoOp) {
|
||||
CARBON_CHECK(target.type_id == sem_ir.insts().Get(expr_id).type_id());
|
||||
return expr_id;
|
||||
}
|
||||
|
||||
// Diagnose unnecessary `ref` tags early, so that they're not obscured by
|
||||
// conversions.
|
||||
if (starting_category == SemIR::ExprCategory::RefTagged &&
|
||||
@@ -2006,15 +2012,15 @@ static auto DiagnoseTypeExprEvaluationFailure(Context& context,
|
||||
|
||||
auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
|
||||
bool diagnose) -> TypeExpr {
|
||||
auto type_inst_id =
|
||||
auto type_as_inst_id =
|
||||
ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeType::TypeId);
|
||||
if (type_inst_id == SemIR::ErrorInst::TypeInstId) {
|
||||
if (type_as_inst_id == SemIR::ErrorInst::InstId) {
|
||||
return {.inst_id = SemIR::ErrorInst::TypeInstId,
|
||||
.type_id = SemIR::ErrorInst::TypeId};
|
||||
}
|
||||
|
||||
auto type_const_id = context.constant_values().Get(type_inst_id);
|
||||
if (!type_const_id.is_constant()) {
|
||||
auto type_as_const_id = context.constant_values().Get(type_as_inst_id);
|
||||
if (!type_as_const_id.is_constant()) {
|
||||
if (diagnose) {
|
||||
DiagnoseTypeExprEvaluationFailure(context, loc_id);
|
||||
}
|
||||
@@ -2022,28 +2028,48 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
|
||||
.type_id = SemIR::ErrorInst::TypeId};
|
||||
}
|
||||
|
||||
return {.inst_id = context.types().GetAsTypeInstId(type_inst_id),
|
||||
.type_id = context.types().GetTypeIdForTypeConstantId(type_const_id)};
|
||||
return {
|
||||
.inst_id = context.types().GetAsTypeInstId(type_as_inst_id),
|
||||
.type_id = context.types().GetTypeIdForTypeConstantId(type_as_const_id)};
|
||||
}
|
||||
|
||||
auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id,
|
||||
auto FormExprAsForm(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId value_id) -> Context::FormExpr {
|
||||
auto form_inst_id =
|
||||
ConvertToValueOfType(context, loc_id, value_id, SemIR::FormType::TypeId);
|
||||
if (form_inst_id == SemIR::ErrorInst::InstId) {
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
|
||||
auto form_const_id = context.constant_values().Get(form_inst_id);
|
||||
if (!form_const_id.is_constant()) {
|
||||
CARBON_DIAGNOSTIC(FormExprEvaluationFailure, Error,
|
||||
"cannot evaluate form expression");
|
||||
context.emitter().Emit(loc_id, FormExprEvaluationFailure);
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
|
||||
auto type_id = GetTypeComponent(context, form_inst_id);
|
||||
auto type_inst_id = context.types().GetTypeInstId(type_id);
|
||||
return {.form_inst_id = form_inst_id,
|
||||
.type_component_inst_id = type_inst_id,
|
||||
.type_component_id = type_id};
|
||||
}
|
||||
|
||||
auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId value_id) -> Context::FormExpr {
|
||||
constexpr Context::FormExpr ErrorFormExpr = {
|
||||
.form_inst_id = SemIR::ErrorInst::InstId,
|
||||
.type_component_inst_id = SemIR::ErrorInst::TypeInstId,
|
||||
.type_component_id = SemIR::ErrorInst::TypeId};
|
||||
auto form_inst_id = SemIR::InstId::None;
|
||||
auto type_inst_id = SemIR::InstId::None;
|
||||
if (auto ref_tag = context.insts().TryGetAs<SemIR::RefTagExpr>(value_id)) {
|
||||
type_inst_id = ConvertToValueOfType(context, loc_id, ref_tag->expr_id,
|
||||
SemIR::TypeType::TypeId);
|
||||
if (type_inst_id == SemIR::ErrorInst::InstId) {
|
||||
return ErrorFormExpr;
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
if (!context.constant_values().Get(type_inst_id).is_constant()) {
|
||||
DiagnoseTypeExprEvaluationFailure(context,
|
||||
SemIR::LocId(ref_tag->expr_id));
|
||||
return ErrorFormExpr;
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
form_inst_id = AddInst(
|
||||
context,
|
||||
@@ -2056,11 +2082,11 @@ auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id,
|
||||
type_inst_id = ConvertToValueOfType(context, loc_id, value_id,
|
||||
SemIR::TypeType::TypeId);
|
||||
if (type_inst_id == SemIR::ErrorInst::InstId) {
|
||||
return ErrorFormExpr;
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
if (!context.constant_values().Get(type_inst_id).is_constant()) {
|
||||
DiagnoseTypeExprEvaluationFailure(context, loc_id);
|
||||
return ErrorFormExpr;
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
form_inst_id = AddInst(
|
||||
context,
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace Carbon::Check {
|
||||
// Description of the target of a conversion.
|
||||
struct ConversionTarget {
|
||||
enum Kind : int8_t {
|
||||
// Perform no conversion. The source expression must already have type
|
||||
// `type_id`.
|
||||
NoOp,
|
||||
// Convert to a value of type `type_id`.
|
||||
Value,
|
||||
// Convert to either a value or a reference of type `type_id`.
|
||||
@@ -187,9 +190,20 @@ inline constexpr TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None,
|
||||
auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
|
||||
bool diagnose = true) -> TypeExpr;
|
||||
|
||||
// Converts an expression for use as a form. If the expression is a type
|
||||
// expression, it is interpreted as an initializing form.
|
||||
auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id,
|
||||
// Converts an expression in a form position for use as a form.
|
||||
//
|
||||
// Note that the right-hand side of a `->` return type declaration is not
|
||||
// a form position for this purpose, because it uses a special syntax to specify
|
||||
// forms. `ReturnExprAsForm` should be used instead in that case.
|
||||
//
|
||||
// `diagnose` has the same effect as in `ExprAsType`.
|
||||
auto FormExprAsForm(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId value_id) -> Context::FormExpr;
|
||||
|
||||
// Evaluates an expression in the return-type position (following `->`, not
|
||||
// `->?`) for use as a form, following the special-case language rules for
|
||||
// evaluating an expression in that position.
|
||||
auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId value_id) -> Context::FormExpr;
|
||||
|
||||
// Handles an expression whose result value is unused.
|
||||
|
||||
@@ -1252,9 +1252,7 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
if (!orig_type_inst_id.has_value()) {
|
||||
context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}",
|
||||
orig_ret_type.getAsString()));
|
||||
return {.form_inst_id = SemIR::ErrorInst::InstId,
|
||||
.type_component_inst_id = SemIR::ErrorInst::TypeInstId,
|
||||
.type_component_id = SemIR::ErrorInst::TypeId};
|
||||
return Context::FormExpr::Error;
|
||||
}
|
||||
Context::FormExpr result = {
|
||||
.form_inst_id = is_reference ? make_ref_form(orig_type_inst_id)
|
||||
@@ -1708,10 +1706,10 @@ static auto ImportVarDecl(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameId var_name_id = AddIdentifierName(context, var_decl->getName());
|
||||
|
||||
// Create an entity name to identify this variable.
|
||||
SemIR::EntityNameId entity_name_id =
|
||||
context.entity_names().AddSymbolicBindingName(
|
||||
var_name_id, GetParentNameScopeId(context, var_decl),
|
||||
SemIR::CompileTimeBindIndex::None, false, /*is_unused=*/false);
|
||||
SemIR::EntityNameId entity_name_id = context.entity_names().Add(
|
||||
{.name_id = var_name_id,
|
||||
.parent_scope_id = GetParentNameScopeId(context, var_decl),
|
||||
.is_unused = false});
|
||||
|
||||
// Create `RefBindingPattern` and `VarPattern`. Mirror the behavior of
|
||||
// import_ref and don't create a `NameBindingDecl` here; we'd never use it for
|
||||
|
||||
@@ -730,6 +730,18 @@ auto EvalConstantInst(Context& /*context*/, SemIR::TupleLiteral inst)
|
||||
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
||||
}
|
||||
|
||||
auto EvalConstantInst(Context& context, SemIR::TypeComponentOf inst)
|
||||
-> ConstantEvalResult {
|
||||
auto form_constant_inst_id =
|
||||
context.constant_values().GetConstantInstId(inst.form_inst_id);
|
||||
if (auto primitive_form = context.insts().TryGetAs<SemIR::AnyPrimitiveForm>(
|
||||
form_constant_inst_id)) {
|
||||
return ConstantEvalResult::Existing(
|
||||
context.constant_values().Get(primitive_form->type_component_id));
|
||||
}
|
||||
return ConstantEvalResult::NewSamePhase(inst);
|
||||
}
|
||||
|
||||
auto EvalConstantInst(Context& context, SemIR::TypeLiteral inst)
|
||||
-> ConstantEvalResult {
|
||||
return ConstantEvalResult::Existing(
|
||||
|
||||
@@ -142,7 +142,7 @@ auto MakeBuiltinFunction(Context& context, SemIR::LocId loc_id,
|
||||
.type_component_inst_id = SemIR::TypeInstId::None,
|
||||
.type_component_id = SemIR::TypeId::None};
|
||||
if (signature.return_type_id.has_value()) {
|
||||
return_form = ExprAsReturnForm(
|
||||
return_form = ReturnExprAsForm(
|
||||
context, loc_id,
|
||||
context.types().GetTypeInstId(signature.return_type_id));
|
||||
return_patterns_id = AddReturnPatterns(context, loc_id, return_form);
|
||||
|
||||
@@ -43,6 +43,8 @@ static auto GetPatternInstKind(Parse::NodeKind node_kind, bool is_ref)
|
||||
: SemIR::InstKind::ValueBindingPattern;
|
||||
case Parse::NodeKind::VarBindingPattern:
|
||||
return SemIR::InstKind::RefBindingPattern;
|
||||
case Parse::NodeKind::FormBindingPattern:
|
||||
return SemIR::InstKind::FormBindingPattern;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected node kind: {0}", node_kind);
|
||||
}
|
||||
@@ -104,17 +106,56 @@ static auto IsValidParamForIntroducer(Context& context, Parse::NodeId node_id,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Information about the expression in the type position of a binding pattern,
|
||||
// i.e. the position following the `:`/`:?`/`:!` separator. Note that this
|
||||
// expression may be interpreted as a type or a form, depending on the binding
|
||||
// kind.
|
||||
struct BindingPatternTypeInfo {
|
||||
// The parse node representing the expression.
|
||||
Parse::AnyExprId node_id;
|
||||
// The inst representing the converted value of that expression. For a `:?`
|
||||
// binding the expression is converted to type `Core.Form`; otherwise it is
|
||||
// converted to type `type`.
|
||||
SemIR::InstId inst_id;
|
||||
// For a `:?` binding this is the type component of the form denoted by
|
||||
// `inst_id`. Otherwise this is the type denoted by `inst_id`.
|
||||
SemIR::TypeId type_component_id;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Handle the type position of a binding pattern.
|
||||
static auto HandleAnyBindingPatternType(Context& context,
|
||||
Parse::NodeKind node_kind)
|
||||
-> BindingPatternTypeInfo {
|
||||
auto [node_id, original_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
|
||||
if (node_kind == Parse::FormBindingPattern::Kind) {
|
||||
auto as_form = FormExprAsForm(context, node_id, original_inst_id);
|
||||
return {.node_id = node_id,
|
||||
.inst_id = as_form.form_inst_id,
|
||||
.type_component_id = as_form.type_component_id};
|
||||
} else {
|
||||
auto as_type = ExprAsType(context, node_id, original_inst_id);
|
||||
return {.node_id = node_id,
|
||||
.inst_id = as_type.inst_id,
|
||||
.type_component_id = as_type.type_id};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this function shorter by factoring pieces out.
|
||||
static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
Parse::NodeKind node_kind,
|
||||
bool is_unused = false) -> bool {
|
||||
// TODO: split this into smaller, more focused functions.
|
||||
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto [cast_type_inst_id, cast_type_id] =
|
||||
ExprAsType(context, type_node, parsed_type_id);
|
||||
auto type_expr = HandleAnyBindingPatternType(context, node_kind);
|
||||
if (context.types()
|
||||
.GetAsInst(type_expr.type_component_id)
|
||||
.Is<SemIR::TypeComponentOf>()) {
|
||||
return context.TODO(node_id, "Support symbolic form bindings");
|
||||
}
|
||||
|
||||
SemIR::ExprRegionId type_expr_region_id =
|
||||
EndSubpatternAsExpr(context, cast_type_inst_id);
|
||||
EndSubpatternAsExpr(context, type_expr.inst_id);
|
||||
|
||||
// The name in a generic binding may be wrapped in `template`.
|
||||
bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern;
|
||||
@@ -139,9 +180,10 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
auto make_binding_pattern = [&]() -> SemIR::InstId {
|
||||
// TODO: Eventually the name will need to support associations with other
|
||||
// scopes, but right now we don't support qualified names here.
|
||||
auto binding = AddBindingPattern(context, name_node, name_id, cast_type_id,
|
||||
type_expr_region_id, pattern_inst_kind,
|
||||
is_template, is_unused);
|
||||
auto binding = AddBindingPattern(
|
||||
context, name_node, name_id, type_expr.type_component_id,
|
||||
context.constant_values().Get(type_expr.inst_id), type_expr_region_id,
|
||||
pattern_inst_kind, is_template, is_unused);
|
||||
|
||||
// TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst
|
||||
// the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`)
|
||||
@@ -168,7 +210,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
"binding pattern has abstract type {0} in `var` "
|
||||
"pattern",
|
||||
SemIR::TypeId);
|
||||
builder.Context(type_node, AbstractTypeInVarPattern, cast_type_id);
|
||||
builder.Context(type_expr.node_id, AbstractTypeInVarPattern,
|
||||
type_expr.type_component_id);
|
||||
};
|
||||
|
||||
// A `self` binding can only appear in an implicit parameter list.
|
||||
@@ -209,7 +252,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
// to fail since `Self` is an incomplete type.
|
||||
if (node_kind == Parse::NodeKind::VarBindingPattern) {
|
||||
auto [unqualified_type_id, qualifiers] =
|
||||
context.types().GetUnqualifiedTypeAndQualifiers(cast_type_id);
|
||||
context.types().GetUnqualifiedTypeAndQualifiers(
|
||||
type_expr.type_component_id);
|
||||
if ((qualifiers & SemIR::TypeQualifiers::Partial) !=
|
||||
SemIR::TypeQualifiers::Partial &&
|
||||
context.types().Is<SemIR::ClassType>(unqualified_type_id)) {
|
||||
@@ -222,7 +266,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
abstract_diagnostic_context);
|
||||
DiagnoseAbstractClass(context, class_type.class_id,
|
||||
/*direct_use=*/true);
|
||||
cast_type_id = SemIR::ErrorInst::TypeId;
|
||||
type_expr.type_component_id = SemIR::ErrorInst::TypeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +277,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
// unless it's nested inside a `var` pattern (because then the
|
||||
// enclosing `var` pattern is), or it's a compile-time binding pattern
|
||||
// (because then it's not passed to the `Call` inst).
|
||||
if (node_kind == Parse::NodeKind::LetBindingPattern) {
|
||||
if (node_kind == Parse::NodeKind::LetBindingPattern ||
|
||||
node_kind == Parse::NodeKind::FormBindingPattern) {
|
||||
auto type_id = context.insts().GetAttachedType(result_inst_id);
|
||||
if (is_ref) {
|
||||
result_inst_id = AddPatternInst<SemIR::RefParamPattern>(
|
||||
@@ -241,6 +286,12 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
{.type_id = type_id,
|
||||
.subpattern_id = result_inst_id,
|
||||
.index = context.full_pattern_stack().NextCallParamIndex()});
|
||||
} else if (node_kind == Parse::NodeKind::FormBindingPattern) {
|
||||
result_inst_id = AddPatternInst<SemIR::FormParamPattern>(
|
||||
context, node_id,
|
||||
{.type_id = type_id,
|
||||
.subpattern_id = result_inst_id,
|
||||
.index = context.full_pattern_stack().NextCallParamIndex()});
|
||||
} else {
|
||||
result_inst_id = AddPatternInst<SemIR::ValueParamPattern>(
|
||||
context, node_id,
|
||||
@@ -259,19 +310,20 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
"binding pattern has incomplete type {0} in name "
|
||||
"binding declaration",
|
||||
InstIdAsType);
|
||||
builder.Context(type_node, IncompleteTypeInBindingDecl,
|
||||
cast_type_inst_id);
|
||||
builder.Context(type_expr.node_id, IncompleteTypeInBindingDecl,
|
||||
type_expr.inst_id);
|
||||
};
|
||||
if (node_kind == Parse::NodeKind::VarBindingPattern) {
|
||||
if (!RequireConcreteType(context, cast_type_id, type_node,
|
||||
incomplete_diagnostic_context,
|
||||
abstract_diagnostic_context)) {
|
||||
cast_type_id = SemIR::ErrorInst::TypeId;
|
||||
if (!RequireConcreteType(
|
||||
context, type_expr.type_component_id, type_expr.node_id,
|
||||
incomplete_diagnostic_context, abstract_diagnostic_context)) {
|
||||
type_expr.type_component_id = SemIR::ErrorInst::TypeId;
|
||||
}
|
||||
} else {
|
||||
if (!RequireCompleteType(context, cast_type_id, type_node,
|
||||
if (!RequireCompleteType(context, type_expr.type_component_id,
|
||||
type_expr.node_id,
|
||||
incomplete_diagnostic_context)) {
|
||||
cast_type_id = SemIR::ErrorInst::TypeId;
|
||||
type_expr.type_component_id = SemIR::ErrorInst::TypeId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,9 +338,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
.Lookup(binding_pattern_id)
|
||||
.value()
|
||||
.bind_name_id;
|
||||
RegisterReturnedVar(context,
|
||||
introducer.modifier_node_id(ModifierOrder::Decl),
|
||||
type_node, cast_type_id, bind_id, name_id);
|
||||
RegisterReturnedVar(
|
||||
context, introducer.modifier_node_id(ModifierOrder::Decl),
|
||||
type_expr.node_id, type_expr.type_component_id, bind_id, name_id);
|
||||
}
|
||||
}
|
||||
context.node_stack().Push(node_id, binding_pattern_id);
|
||||
@@ -312,7 +364,8 @@ auto HandleParseNode(Context& context, Parse::VarBindingPatternId node_id)
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FormBindingPatternId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement :? support");
|
||||
return HandleAnyBindingPattern(context, node_id,
|
||||
Parse::NodeKind::FormBindingPattern);
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context,
|
||||
|
||||
@@ -3,38 +3,63 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/handle.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
#include "toolchain/parse/node_category.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::RefPrimitiveFormId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto type_expr = ExprAsType(context, type_node_id, type_inst_id);
|
||||
auto inst_id =
|
||||
AddInst<SemIR::RefForm>(context, node_id,
|
||||
{.type_id = SemIR::FormType::TypeId,
|
||||
.type_component_inst_id = type_expr.inst_id});
|
||||
context.node_stack().Push(node_id, inst_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ValPrimitiveFormId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto type_expr = ExprAsType(context, type_node_id, type_inst_id);
|
||||
auto inst_id =
|
||||
AddInst<SemIR::ValueForm>(context, node_id,
|
||||
{.type_id = SemIR::FormType::TypeId,
|
||||
.type_component_inst_id = type_expr.inst_id});
|
||||
context.node_stack().Push(node_id, inst_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::VarPrimitiveFormId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto type_expr = ExprAsType(context, type_node_id, type_inst_id);
|
||||
auto inst_id =
|
||||
AddInst<SemIR::InitForm>(context, node_id,
|
||||
{.type_id = SemIR::FormType::TypeId,
|
||||
.type_component_inst_id = type_expr.inst_id,
|
||||
.index = SemIR::CallParamIndex::None});
|
||||
context.node_stack().Push(node_id, inst_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FormLiteralKeywordId node_id)
|
||||
auto HandleParseNode(Context& /*context*/,
|
||||
Parse::FormLiteralKeywordId /*node_id*/) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& /*context*/,
|
||||
Parse::FormLiteralOpenParenId /*node_id*/) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& /*context*/, Parse::FormLiteralId /*node_id*/)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FormLiteralOpenParenId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FormLiteralId node_id) -> bool {
|
||||
return context.TODO(node_id, "Implement form literals");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -53,7 +53,7 @@ auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool {
|
||||
auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
|
||||
// Propagate the type expression.
|
||||
auto form_expr = ExprAsReturnForm(context, type_node_id, type_inst_id);
|
||||
auto form_expr = ReturnExprAsForm(context, type_node_id, type_inst_id);
|
||||
context.PushReturnForm(form_expr);
|
||||
auto return_patterns_id = AddReturnPatterns(context, node_id, form_expr);
|
||||
context.node_stack().Push(node_id, return_patterns_id);
|
||||
|
||||
@@ -249,6 +249,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
|
||||
}
|
||||
|
||||
switch (new_param_pattern.kind()) {
|
||||
case SemIR::FormParamPattern::Kind:
|
||||
case SemIR::OutParamPattern::Kind:
|
||||
case SemIR::RefParamPattern::Kind:
|
||||
case SemIR::ValueParamPattern::Kind:
|
||||
@@ -267,6 +268,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
|
||||
.new_id =
|
||||
new_param_pattern.As<SemIR::VarPattern>().subpattern_id});
|
||||
break;
|
||||
case SemIR::FormBindingPattern::Kind:
|
||||
case SemIR::RefBindingPattern::Kind:
|
||||
case SemIR::SymbolicBindingPattern::Kind:
|
||||
case SemIR::ValueBindingPattern::Kind: {
|
||||
|
||||
@@ -48,11 +48,15 @@ auto EndSubpatternAsNonExpr(Context& context) -> void {
|
||||
|
||||
auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
|
||||
SemIR::NameId name_id, SemIR::TypeId type_id,
|
||||
SemIR::ConstantId form_id,
|
||||
SemIR::ExprRegionId type_region_id,
|
||||
SemIR::InstKind pattern_kind, bool is_template,
|
||||
bool is_unused) -> BindingPatternInfo {
|
||||
SemIR::InstKind bind_name_kind;
|
||||
switch (pattern_kind) {
|
||||
case SemIR::InstKind::FormBindingPattern:
|
||||
bind_name_kind = SemIR::InstKind::FormBinding;
|
||||
break;
|
||||
case SemIR::InstKind::RefBindingPattern:
|
||||
bind_name_kind = SemIR::InstKind::RefBinding;
|
||||
break;
|
||||
@@ -63,15 +67,23 @@ auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
|
||||
bind_name_kind = SemIR::InstKind::ValueBinding;
|
||||
break;
|
||||
default:
|
||||
CARBON_FATAL("pattern_kind is not a binding pattern kind");
|
||||
CARBON_FATAL("pattern_kind {0} is not a binding pattern kind",
|
||||
pattern_kind);
|
||||
}
|
||||
bool is_generic = pattern_kind == SemIR::SymbolicBindingPattern::Kind;
|
||||
|
||||
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, is_unused || name_id == SemIR::NameId::Underscore);
|
||||
SemIR::EntityName entity_name = {
|
||||
.name_id = name_id,
|
||||
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
|
||||
.is_unused = is_unused || name_id == SemIR::NameId::Underscore};
|
||||
if (is_generic) {
|
||||
entity_name.bind_index_value =
|
||||
context.scope_stack().AddCompileTimeBinding().index;
|
||||
entity_name.is_template = is_template;
|
||||
} else if (pattern_kind == SemIR::InstKind::FormBindingPattern) {
|
||||
entity_name.form_id = form_id;
|
||||
}
|
||||
auto entity_name_id = context.entity_names().Add(entity_name);
|
||||
|
||||
auto bind_id = AddInstInNoBlock(
|
||||
context,
|
||||
@@ -145,8 +157,9 @@ auto AddParamPattern(Context& context, SemIR::LocId loc_id,
|
||||
const auto& binding_pattern_kind = is_ref ? SemIR::RefBindingPattern::Kind
|
||||
: SemIR::ValueBindingPattern::Kind;
|
||||
SemIR::InstId pattern_id =
|
||||
AddBindingPattern(context, loc_id, name_id, type_id, type_expr_region_id,
|
||||
binding_pattern_kind,
|
||||
AddBindingPattern(context, loc_id, name_id, type_id,
|
||||
/*form_id=*/SemIR::ConstantId::None,
|
||||
type_expr_region_id, binding_pattern_kind,
|
||||
/*is_template=*/false, /*is_unused=*/false)
|
||||
.pattern_id;
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ struct BindingPatternInfo {
|
||||
// bindings.
|
||||
auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
|
||||
SemIR::NameId name_id, SemIR::TypeId type_id,
|
||||
SemIR::ConstantId form_id,
|
||||
SemIR::ExprRegionId type_region_id,
|
||||
SemIR::InstKind pattern_kind, bool is_template,
|
||||
bool is_unused) -> BindingPatternInfo;
|
||||
|
||||
+178
-153
@@ -97,21 +97,11 @@ class MatchContext {
|
||||
auto EmitPatternMatch(Context& context, MatchContext::WorkItem entry) -> void;
|
||||
|
||||
// Implementations of `EmitPatternMatch` for particular pattern inst kinds.
|
||||
// The pattern argument is always equal to
|
||||
// `context.insts().Get(entry.pattern_id)`.
|
||||
auto DoEmitPatternMatch(Context& context,
|
||||
SemIR::AnyBindingPattern binding_pattern,
|
||||
WorkItem entry) -> void;
|
||||
auto DoEmitPatternMatch(Context& context,
|
||||
SemIR::ValueParamPattern param_pattern,
|
||||
WorkItem entry) -> void;
|
||||
template <typename RefParamPatternT>
|
||||
requires std::is_same_v<RefParamPatternT, SemIR::RefParamPattern> ||
|
||||
std::is_same_v<RefParamPatternT, SemIR::VarParamPattern>
|
||||
auto DoEmitPatternMatch(Context& context, RefParamPatternT param_pattern,
|
||||
WorkItem entry) -> void;
|
||||
auto DoEmitPatternMatch(Context& context,
|
||||
SemIR::OutParamPattern param_pattern, WorkItem entry)
|
||||
SemIR::AnyParamPattern param_pattern, WorkItem entry)
|
||||
-> void;
|
||||
auto DoEmitPatternMatch(Context& context,
|
||||
SemIR::ReturnSlotPattern return_slot_pattern,
|
||||
@@ -121,6 +111,15 @@ class MatchContext {
|
||||
auto DoEmitPatternMatch(Context& context, SemIR::TuplePattern tuple_pattern,
|
||||
WorkItem entry) -> void;
|
||||
|
||||
// Performs the core logic of matching a variable pattern whose type is
|
||||
// `pattern_type_id`, but returns the scrutinee that its subpattern should be
|
||||
// matched with, rather than pushing it onto the worklist. This is factored
|
||||
// out so it can be reused when handling a `FormBindingPattern` or
|
||||
// `FormParamPattern` with an initializing form.
|
||||
auto DoEmitVarPatternMatchImpl(Context& context,
|
||||
SemIR::TypeId pattern_type_id,
|
||||
WorkItem entry) const -> SemIR::InstId;
|
||||
|
||||
// The stack of work to be processed.
|
||||
llvm::SmallVector<WorkItem> stack_;
|
||||
|
||||
@@ -241,6 +240,75 @@ static auto InsertHere(Context& context, SemIR::ExprRegionId region_id)
|
||||
return region.result_id;
|
||||
}
|
||||
|
||||
// Returns the kind of conversion to perform on the scrutinee when matching the
|
||||
// given pattern. `form_kind` is the form of the pattern, if known; it only
|
||||
// affects the behavior of `FormBindingPattern` and `FormParamPattern`,
|
||||
// and it must be set in the `FormParamPattern` case.
|
||||
static auto ConversionKindFor(
|
||||
Context& context, SemIR::Inst pattern, MatchContext::WorkItem entry,
|
||||
std::optional<SemIR::InstKind> form_kind = std::nullopt)
|
||||
-> ConversionTarget::Kind {
|
||||
CARBON_KIND_SWITCH(pattern) {
|
||||
case SemIR::OutParamPattern::Kind:
|
||||
case SemIR::VarParamPattern::Kind:
|
||||
return ConversionTarget::NoOp;
|
||||
case SemIR::RefBindingPattern::Kind:
|
||||
return ConversionTarget::DurableRef;
|
||||
case SemIR::RefParamPattern::Kind:
|
||||
return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam
|
||||
: ConversionTarget::RefParam;
|
||||
case SemIR::SymbolicBindingPattern::Kind:
|
||||
case SemIR::ValueBindingPattern::Kind:
|
||||
case SemIR::ValueParamPattern::Kind:
|
||||
return ConversionTarget::Value;
|
||||
case CARBON_KIND(SemIR::FormBindingPattern form_binding_pattern): {
|
||||
if (!form_kind) {
|
||||
auto form_id = context.entity_names()
|
||||
.Get(form_binding_pattern.entity_name_id)
|
||||
.form_id;
|
||||
auto form_inst_id = context.constant_values().GetInstId(form_id);
|
||||
form_kind = context.insts().Get(form_inst_id).kind();
|
||||
}
|
||||
|
||||
switch (*form_kind) {
|
||||
case SemIR::InitForm::Kind:
|
||||
context.TODO(entry.pattern_id, "Support local initializing forms");
|
||||
[[fallthrough]];
|
||||
case SemIR::RefForm::Kind:
|
||||
return ConversionTarget::DurableRef;
|
||||
case SemIR::SymbolicBinding::Kind:
|
||||
context.TODO(entry.pattern_id, "Support symbolic form bindings");
|
||||
[[fallthrough]];
|
||||
case SemIR::ValueForm::Kind:
|
||||
return ConversionTarget::Value;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected form kind {0}", form_kind);
|
||||
}
|
||||
}
|
||||
case SemIR::FormParamPattern::Kind: {
|
||||
CARBON_CHECK(form_kind);
|
||||
switch (*form_kind) {
|
||||
case SemIR::InitForm::Kind:
|
||||
return ConversionTarget::NoOp;
|
||||
case SemIR::RefForm::Kind:
|
||||
// TODO: Figure out rules for when the argument must have a `ref` tag.
|
||||
return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam
|
||||
: ConversionTarget::RefParam;
|
||||
case SemIR::SymbolicBinding::Kind:
|
||||
context.TODO(entry.pattern_id, "Support symbolic form params");
|
||||
[[fallthrough]];
|
||||
case SemIR::ErrorInst::Kind:
|
||||
case SemIR::ValueForm::Kind:
|
||||
return ConversionTarget::Value;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected form kind {0}", form_kind);
|
||||
}
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL("Unexpected pattern kind in {0}", pattern);
|
||||
}
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
SemIR::AnyBindingPattern binding_pattern,
|
||||
MatchContext::WorkItem entry) -> void {
|
||||
@@ -262,17 +330,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
InsertHere(context, type_expr_region_id);
|
||||
auto value_id = SemIR::InstId::None;
|
||||
if (kind_ == MatchKind::Local) {
|
||||
auto conversion_kind = [&binding_pattern]() -> ConversionTarget::Kind {
|
||||
switch (binding_pattern.kind) {
|
||||
case SemIR::SymbolicBindingPattern::Kind:
|
||||
case SemIR::ValueBindingPattern::Kind:
|
||||
return ConversionTarget::Value;
|
||||
case SemIR::RefBindingPattern::Kind:
|
||||
return ConversionTarget::DurableRef;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected inst kind {0}", binding_pattern.kind);
|
||||
}
|
||||
}();
|
||||
auto conversion_kind = ConversionKindFor(context, binding_pattern, entry);
|
||||
|
||||
if (!bind_name_id.has_value()) {
|
||||
// TODO: Is this appropriate, or should we perform a conversion based on
|
||||
@@ -298,9 +356,67 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the inst kind to use for the parameter corresponding to the given
|
||||
// parameter pattern. If the pattern is a `FormParamPattern`, `form_kind`
|
||||
// must be the pattern's form; otherwise it is ignored.
|
||||
static auto ParamKindFor(
|
||||
Context& context, SemIR::Inst param_pattern, MatchContext::WorkItem entry,
|
||||
std::optional<SemIR::InstKind> form_kind = std::nullopt)
|
||||
-> SemIR::InstKind {
|
||||
switch (param_pattern.kind()) {
|
||||
case SemIR::OutParamPattern::Kind:
|
||||
return SemIR::OutParam::Kind;
|
||||
case SemIR::RefParamPattern::Kind:
|
||||
case SemIR::VarParamPattern::Kind:
|
||||
return SemIR::RefParam::Kind;
|
||||
case SemIR::ValueParamPattern::Kind:
|
||||
return SemIR::ValueParam::Kind;
|
||||
case SemIR::FormParamPattern::Kind:
|
||||
CARBON_CHECK(form_kind);
|
||||
switch (*form_kind) {
|
||||
case SemIR::InitForm::Kind:
|
||||
case SemIR::RefForm::Kind:
|
||||
return SemIR::RefParam::Kind;
|
||||
case SemIR::SymbolicBinding::Kind:
|
||||
context.TODO(entry.pattern_id, "Support symbolic form params");
|
||||
[[fallthrough]];
|
||||
case SemIR::ErrorInst::Kind:
|
||||
case SemIR::ValueForm::Kind:
|
||||
return SemIR::ValueParam::Kind;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected form kind {0}", form_kind);
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL("Unexpected param pattern kind: {0}", param_pattern);
|
||||
}
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
SemIR::ValueParamPattern param_pattern,
|
||||
SemIR::AnyParamPattern param_pattern,
|
||||
WorkItem entry) -> void {
|
||||
// If this is a FormParamPattern, determine its form.
|
||||
std::optional<SemIR::InstKind> form_kind;
|
||||
if (param_pattern.kind == SemIR::FormParamPattern::Kind) {
|
||||
if (param_pattern.subpattern_id == SemIR::ErrorInst::InstId) {
|
||||
form_kind = SemIR::ErrorInst::Kind;
|
||||
} else {
|
||||
auto binding_pattern = context.insts().GetAs<SemIR::FormBindingPattern>(
|
||||
param_pattern.subpattern_id);
|
||||
auto form_id =
|
||||
context.entity_names().Get(binding_pattern.entity_name_id).form_id;
|
||||
auto form_inst_id = context.constant_values().GetInstId(form_id);
|
||||
form_kind = context.insts().Get(form_inst_id).kind();
|
||||
|
||||
// If the form is initializing, match this as a `VarPattern` before
|
||||
// matching it as a parameter pattern.
|
||||
if (form_kind == SemIR::InitForm::Kind) {
|
||||
auto new_scrutinee_id =
|
||||
DoEmitVarPatternMatchImpl(context, param_pattern.type_id, entry);
|
||||
entry.scrutinee_id = new_scrutinee_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (kind_) {
|
||||
case MatchKind::Caller: {
|
||||
CARBON_CHECK(
|
||||
@@ -311,25 +427,31 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
if (entry.scrutinee_id == SemIR::ErrorInst::InstId) {
|
||||
call_args_.push_back(SemIR::ErrorInst::InstId);
|
||||
} else {
|
||||
call_args_.push_back(ConvertToValueOfType(
|
||||
auto scrutinee_type_id = ExtractScrutineeType(
|
||||
context.sem_ir(),
|
||||
SemIR::GetTypeOfInstInSpecific(
|
||||
context.sem_ir(), callee_specific_id_, entry.pattern_id));
|
||||
call_args_.push_back(Convert(
|
||||
context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id,
|
||||
ExtractScrutineeType(
|
||||
context.sem_ir(),
|
||||
SemIR::GetTypeOfInstInSpecific(
|
||||
context.sem_ir(), callee_specific_id_, entry.pattern_id))));
|
||||
{.kind =
|
||||
ConversionKindFor(context, param_pattern, entry, form_kind),
|
||||
.type_id = scrutinee_type_id}));
|
||||
}
|
||||
// Do not traverse farther, because the caller side of the pattern
|
||||
// ends here.
|
||||
break;
|
||||
}
|
||||
case MatchKind::Callee: {
|
||||
auto param_id = AddInst<SemIR::ValueParam>(
|
||||
context, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id =
|
||||
ExtractScrutineeType(context.sem_ir(), param_pattern.type_id),
|
||||
.index = param_pattern.index,
|
||||
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
|
||||
context.sem_ir(), entry.pattern_id)});
|
||||
SemIR::AnyParam param = {
|
||||
.kind = ParamKindFor(context, param_pattern, entry, form_kind),
|
||||
.type_id =
|
||||
ExtractScrutineeType(context.sem_ir(), param_pattern.type_id),
|
||||
.index = param_pattern.index,
|
||||
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
|
||||
context.sem_ir(), entry.pattern_id)};
|
||||
auto param_id =
|
||||
AddInst(context, SemIR::LocIdAndInst::UncheckedLoc(
|
||||
SemIR::LocId(entry.pattern_id), param));
|
||||
AddWork({.pattern_id = param_pattern.subpattern_id,
|
||||
.scrutinee_id = param_id});
|
||||
call_params_.push_back(param_id);
|
||||
@@ -342,100 +464,6 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RefParamPatternT>
|
||||
requires std::is_same_v<RefParamPatternT, SemIR::RefParamPattern> ||
|
||||
std::is_same_v<RefParamPatternT, SemIR::VarParamPattern>
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
RefParamPatternT param_pattern,
|
||||
WorkItem entry) -> void {
|
||||
switch (kind_) {
|
||||
case MatchKind::Caller: {
|
||||
CARBON_CHECK(
|
||||
static_cast<size_t>(param_pattern.index.index) == call_args_.size(),
|
||||
"Parameters out of order; expecting {0} but got {1}",
|
||||
call_args_.size(), param_pattern.index.index);
|
||||
CARBON_CHECK(entry.scrutinee_id.has_value());
|
||||
|
||||
if (std::is_same_v<RefParamPatternT, SemIR::VarParamPattern>) {
|
||||
call_args_.push_back(entry.scrutinee_id);
|
||||
break;
|
||||
}
|
||||
auto scrutinee_type_id = ExtractScrutineeType(
|
||||
context.sem_ir(),
|
||||
SemIR::GetTypeOfInstInSpecific(context.sem_ir(), callee_specific_id_,
|
||||
entry.pattern_id));
|
||||
call_args_.push_back(Convert(
|
||||
context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id,
|
||||
{.kind = entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam
|
||||
: ConversionTarget::RefParam,
|
||||
.type_id = scrutinee_type_id}));
|
||||
// Do not traverse farther, because the caller side of the pattern
|
||||
// ends here.
|
||||
break;
|
||||
}
|
||||
case MatchKind::Callee: {
|
||||
auto param_id = AddInst<SemIR::RefParam>(
|
||||
context, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id =
|
||||
ExtractScrutineeType(context.sem_ir(), param_pattern.type_id),
|
||||
.index = param_pattern.index,
|
||||
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
|
||||
context.sem_ir(), entry.pattern_id)});
|
||||
AddWork({.pattern_id = param_pattern.subpattern_id,
|
||||
.scrutinee_id = param_id});
|
||||
call_params_.push_back(param_id);
|
||||
call_param_patterns_.push_back(entry.pattern_id);
|
||||
break;
|
||||
}
|
||||
case MatchKind::Local: {
|
||||
CARBON_FATAL("Found RefParamPattern during local pattern match");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
SemIR::OutParamPattern param_pattern,
|
||||
WorkItem entry) -> void {
|
||||
switch (kind_) {
|
||||
case MatchKind::Caller: {
|
||||
CARBON_CHECK(
|
||||
static_cast<size_t>(param_pattern.index.index) == call_args_.size(),
|
||||
"Parameters out of order; expecting {0} but got {1}",
|
||||
call_args_.size(), param_pattern.index.index);
|
||||
CARBON_CHECK(entry.scrutinee_id.has_value());
|
||||
CARBON_CHECK(
|
||||
context.insts().Get(entry.scrutinee_id).type_id() ==
|
||||
ExtractScrutineeType(
|
||||
context.sem_ir(),
|
||||
SemIR::GetTypeOfInstInSpecific(
|
||||
context.sem_ir(), callee_specific_id_, entry.pattern_id)));
|
||||
call_args_.push_back(entry.scrutinee_id);
|
||||
// Do not traverse farther, because the caller side of the pattern
|
||||
// ends here.
|
||||
break;
|
||||
}
|
||||
case MatchKind::Callee: {
|
||||
// TODO: Consider ways to address near-duplication with the
|
||||
// other ParamPattern cases.
|
||||
auto param_id = AddInst<SemIR::OutParam>(
|
||||
context, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id =
|
||||
ExtractScrutineeType(context.sem_ir(), param_pattern.type_id),
|
||||
.index = param_pattern.index,
|
||||
.pretty_name_id = SemIR::GetPrettyNameFromPatternId(
|
||||
context.sem_ir(), entry.pattern_id)});
|
||||
AddWork({.pattern_id = param_pattern.subpattern_id,
|
||||
.scrutinee_id = param_id});
|
||||
call_param_patterns_.push_back(entry.pattern_id);
|
||||
call_params_.push_back(param_id);
|
||||
break;
|
||||
}
|
||||
case MatchKind::Local: {
|
||||
CARBON_FATAL("Found OutParamPattern during local pattern match");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitPatternMatch(
|
||||
Context& context, SemIR::ReturnSlotPattern return_slot_pattern,
|
||||
WorkItem entry) -> void {
|
||||
@@ -457,15 +485,23 @@ auto MatchContext::DoEmitPatternMatch(
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
SemIR::VarPattern var_pattern,
|
||||
WorkItem entry) -> void {
|
||||
auto new_scrutinee_id =
|
||||
DoEmitVarPatternMatchImpl(context, var_pattern.type_id, entry);
|
||||
AddWork({.pattern_id = var_pattern.subpattern_id,
|
||||
.scrutinee_id = new_scrutinee_id});
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitVarPatternMatchImpl(Context& context,
|
||||
SemIR::TypeId pattern_type_id,
|
||||
WorkItem entry) const
|
||||
-> SemIR::InstId {
|
||||
auto storage_id = SemIR::InstId::None;
|
||||
switch (kind_) {
|
||||
case MatchKind::Callee: {
|
||||
// We're emitting pattern-match IR for the callee, but we're still on
|
||||
// the caller side of the pattern, so we traverse without emitting any
|
||||
// insts.
|
||||
AddWork({.pattern_id = var_pattern.subpattern_id,
|
||||
.scrutinee_id = SemIR::InstId::None});
|
||||
return;
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
case MatchKind::Local: {
|
||||
// In a `var`/`let` declaration, the `VarStorage` inst is created before
|
||||
@@ -478,8 +514,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
case MatchKind::Caller: {
|
||||
storage_id = AddInst<SemIR::TemporaryStorage>(
|
||||
context, SemIR::LocId(entry.pattern_id),
|
||||
{.type_id =
|
||||
ExtractScrutineeType(context.sem_ir(), var_pattern.type_id)});
|
||||
{.type_id = ExtractScrutineeType(context.sem_ir(), pattern_type_id)});
|
||||
CARBON_CHECK(entry.scrutinee_id.has_value());
|
||||
break;
|
||||
}
|
||||
@@ -511,11 +546,10 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
{.lhs_id = storage_id, .rhs_id = init_id});
|
||||
}
|
||||
}
|
||||
AddWork(
|
||||
{.pattern_id = var_pattern.subpattern_id, .scrutinee_id = storage_id});
|
||||
if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
context.global_init().Suspend();
|
||||
}
|
||||
return storage_id;
|
||||
}
|
||||
|
||||
auto MatchContext::DoEmitPatternMatch(Context& context,
|
||||
@@ -606,27 +640,18 @@ auto MatchContext::EmitPatternMatch(Context& context,
|
||||
CARBON_KIND_SWITCH(pattern) {
|
||||
case SemIR::RefBindingPattern::Kind:
|
||||
case SemIR::SymbolicBindingPattern::Kind:
|
||||
case SemIR::ValueBindingPattern::Kind: {
|
||||
case SemIR::ValueBindingPattern::Kind:
|
||||
case SemIR::FormBindingPattern::Kind:
|
||||
DoEmitPatternMatch(context, pattern.As<SemIR::AnyBindingPattern>(),
|
||||
entry);
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::ValueParamPattern param_pattern): {
|
||||
DoEmitPatternMatch(context, param_pattern, entry);
|
||||
case SemIR::FormParamPattern::Kind:
|
||||
case SemIR::RefParamPattern::Kind:
|
||||
case SemIR::ValueParamPattern::Kind:
|
||||
case SemIR::VarParamPattern::Kind:
|
||||
case SemIR::OutParamPattern::Kind:
|
||||
DoEmitPatternMatch(context, pattern.As<SemIR::AnyParamPattern>(), entry);
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::RefParamPattern param_pattern): {
|
||||
DoEmitPatternMatch(context, param_pattern, entry);
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::VarParamPattern param_pattern): {
|
||||
DoEmitPatternMatch(context, param_pattern, entry);
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::OutParamPattern param_pattern): {
|
||||
DoEmitPatternMatch(context, param_pattern, entry);
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): {
|
||||
DoEmitPatternMatch(context, return_slot_pattern, entry);
|
||||
break;
|
||||
|
||||
@@ -66,10 +66,10 @@ fn G(x: Cpp.X) {
|
||||
// CHECK:STDOUT: name_scope60000001: {inst: inst60000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name2: inst60000014, name3: inst6000002A, name4: inst6000004E}}
|
||||
// CHECK:STDOUT: name_scope60000002: {inst: inst60000014, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}}
|
||||
// CHECK:STDOUT: entity_names:
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000002: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000003: {name: name4, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000002: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000003: {name: name4, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: cpp_global_vars:
|
||||
// CHECK:STDOUT: cpp_global_var60000000: {key: {entity_name_id: entity_name60000003}, clang_decl_id: clang_decl_id60000007}
|
||||
// CHECK:STDOUT: functions:
|
||||
|
||||
@@ -121,7 +121,7 @@ fn B() {
|
||||
// CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst50000011, name0: inst50000012}}
|
||||
// CHECK:STDOUT: name_scope50000001: {inst: inst50000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst50000017}}
|
||||
// CHECK:STDOUT: entity_names:
|
||||
// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: cpp_global_vars: {}
|
||||
// CHECK:STDOUT: functions:
|
||||
// CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]}
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ fn B() {
|
||||
// CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst50000011, name0: inst50000012}}
|
||||
// CHECK:STDOUT: name_scope50000001: {inst: inst50000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst50000017}}
|
||||
// CHECK:STDOUT: entity_names:
|
||||
// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: cpp_global_vars: {}
|
||||
// CHECK:STDOUT: functions:
|
||||
// CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]}
|
||||
|
||||
+64
-64
@@ -270,70 +270,70 @@ fn Foo[T:! type](p: T*) -> (T*, ()) {
|
||||
// CHECK:STDOUT: name_scope6000000C: {inst: inst600000DB, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}}
|
||||
// CHECK:STDOUT: name_scope6000000D: {inst: inst6000011F, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}}
|
||||
// CHECK:STDOUT: entity_names:
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name(PeriodSelf), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000002: {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000003: {name: name3, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000004: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000005: {name: name4, parent_scope: name_scope60000003, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000006: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000007: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000008: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000009: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000A: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000B: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000C: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000D: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000E: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000000F: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000010: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000011: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000012: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000013: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000014: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000015: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000016: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000017: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000018: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000019: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001A: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001B: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001C: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001D: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001E: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000001F: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000020: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000021: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000022: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000023: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000024: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000025: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000026: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000027: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000028: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000029: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002A: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002B: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002C: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002D: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002E: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000002F: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000030: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000031: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000032: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000033: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000034: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000035: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000036: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000037: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000038: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000039: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003A: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003B: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003C: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003D: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003E: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name6000003F: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name(PeriodSelf), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000002: {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000003: {name: name3, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000004: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000005: {name: name4, parent_scope: name_scope60000003, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000006: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000007: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000008: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000009: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000A: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000B: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000C: {name: name(SelfType), parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000D: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000E: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000000F: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000010: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000011: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000012: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000013: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000014: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000015: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000016: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000017: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000018: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000019: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001A: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001B: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001C: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001D: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001E: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000001F: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000020: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000021: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000022: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000023: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000024: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000025: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000026: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000027: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000028: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000029: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002A: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002B: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002C: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002D: {name: name(SelfValue), parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002E: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000002F: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000030: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000031: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000032: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000033: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000034: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000035: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000036: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000037: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000038: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name60000039: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003A: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003B: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003C: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003D: {name: name6, parent_scope: name_scope<none>, index: 2, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003E: {name: name5, parent_scope: name_scope<none>, index: 1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: entity_name6000003F: {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: cpp_global_vars: {}
|
||||
// CHECK:STDOUT: functions:
|
||||
// CHECK:STDOUT: function60000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block60000011, call_params_id: inst_block60000012, return_type_inst_id: inst60000030, return_form_inst_id: inst60000032, return_patterns_id: inst_block60000010, body: [inst_block60000019]}
|
||||
|
||||
@@ -31,7 +31,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: name_scopes:
|
||||
// CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst6000002A}}
|
||||
// CHECK:STDOUT: entity_names:
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0}
|
||||
// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0, is_unused: 0, form: constant<none>}
|
||||
// CHECK:STDOUT: cpp_global_vars: {}
|
||||
// CHECK:STDOUT: functions:
|
||||
// CHECK:STDOUT: function60000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block6000000C, call_params_id: inst_block6000000D, return_type_inst_id: inst60000020, return_form_inst_id: inst60000021, return_patterns_id: inst_block6000000B, body: [inst_block60000010]}
|
||||
|
||||
+419
@@ -0,0 +1,419 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/form.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/form.carbon
|
||||
|
||||
// --- ref_form_param.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F(x:? form(ref i32));
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
var y: i32 = 0;
|
||||
//@dump-sem-ir-begin
|
||||
F(ref y);
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- var_form_param.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F(x:? form(var i32));
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
//@dump-sem-ir-begin
|
||||
F(0);
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- val_form_param.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F(x:? form(val i32));
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
//@dump-sem-ir-begin
|
||||
F(0);
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- type_component_needs_conversion.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F(x:? form(ref ()));
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- fail_param_form_not_constant.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> Core.Form();
|
||||
// CHECK:STDERR: fail_param_form_not_constant.carbon:[[@LINE+4]]:10: error: cannot evaluate form expression [FormExprEvaluationFailure]
|
||||
// CHECK:STDERR: fn G(x:? F());
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
fn G(x:? F());
|
||||
|
||||
// --- fail_form_param_not_form.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure]
|
||||
// CHECK:STDERR: fn F(x:? i32);
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:10: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext]
|
||||
// CHECK:STDERR: fn F(x:? i32);
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
fn F(x:? i32);
|
||||
// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+8]]:6: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn G(ref x:? i32);
|
||||
// CHECK:STDERR: ^~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:6: error: expected `:` binding after `ref` [ExpectedRuntimeBindingPatternAfterRef]
|
||||
// CHECK:STDERR: fn G(ref x:? i32);
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
fn G(ref x:? i32);
|
||||
|
||||
// --- fail_missing_ref_tag.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(x:? form(ref i32));
|
||||
|
||||
fn G() {
|
||||
var y: i32 = 0;
|
||||
// CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE+7]]:5: error: argument to `ref` parameter not marked with `ref` [RefParamNoRefTag]
|
||||
// CHECK:STDERR: F(y);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE-7]]:6: note: initializing function parameter [InCallToFunctionParam]
|
||||
// CHECK:STDERR: fn F(x:? form(ref i32));
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
F(y);
|
||||
}
|
||||
|
||||
// --- fail_todo_form_generic_param.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_todo_form_generic_param.carbon:[[@LINE+4]]:26: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F(Form:! Core.Form(), x:? Form);
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F(Form:! Core.Form(), x:? Form);
|
||||
|
||||
fn G() {
|
||||
var x: i32;
|
||||
F(form(var i32), x);
|
||||
}
|
||||
|
||||
// --- fail_todo_local_init_form_binding.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+12]]:7: error: semantics TODO: `Support local initializing forms` [SemanticsTodo]
|
||||
// CHECK:STDERR: let x:? form(var i32) = 0;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+8]]:27: error: cannot bind durable reference to non-reference value of type `i32` [ConversionFailureNonRefToRef]
|
||||
// CHECK:STDERR: let x:? form(var i32) = 0;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+4]]:7: warning: binding `x` unused [UnusedBinding]
|
||||
// CHECK:STDERR: let x:? form(var i32) = 0;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
let x:? form(var i32) = 0;
|
||||
}
|
||||
|
||||
// --- fail_todo_local_symbolic_form_binding.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// TODO: this code should fail for a different reason, but right now we don't
|
||||
// have a way to write correct code that reaches the TODO we're exercising here.
|
||||
fn F(Form:! Core.Form()) {
|
||||
// CHECK:STDERR: fail_todo_local_symbolic_form_binding.carbon:[[@LINE+4]]:7: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo]
|
||||
// CHECK:STDERR: let y:? Form = 0;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
let y:? Form = 0;
|
||||
}
|
||||
|
||||
// --- fail_todo_ref_return_form.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: fail_todo_ref_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F() ->? form(ref i32);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() ->? form(ref i32);
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
//@dump-sem-ir-begin
|
||||
let ref x: i32 = F();
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- fail_todo_var_return_form.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: fail_todo_var_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F() ->? form(var i32);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() ->? form(var i32);
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
//@dump-sem-ir-begin
|
||||
let var x: i32 = F();
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- fail_todo_val_return_form.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
// CHECK:STDERR: fail_todo_val_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F() ->? form(val i32);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() ->? form(val i32);
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() {
|
||||
//@dump-sem-ir-begin
|
||||
let x: i32 = F();
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- fail_return_form_not_form.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F() ->? i32;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() ->? i32;
|
||||
fn F() ->? ref i32;
|
||||
|
||||
// CHECK:STDOUT: --- ref_form_param.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: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete]
|
||||
// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.1da] {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %i32 [concrete = constants.%.1da]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc4_6.2: ref %i32 = form_binding x, %x.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%x.param: ref %i32);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %y.ref: ref %i32 = name_ref y, %y
|
||||
// CHECK:STDOUT: %.loc10: %i32 = ref_tag %y.ref
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc10)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- var_form_param.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: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %.324: Core.Form = init_form %i32, call_param<none> [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.e8c: 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.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete]
|
||||
// CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete]
|
||||
// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.324] {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc4_15.2: Core.Form = init_form %i32, call_param<none> [concrete = constants.%.324]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc4_6.2: ref %i32 = form_binding x, %x.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%x.param: ref %i32);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %.loc4_7.1: ref %i32 = temporary_storage
|
||||
// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5]
|
||||
// CHECK:STDOUT: %bound_method.loc4_7.1: <bound method> = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc4_7.2: <bound method> = bound_method %int_0, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc4_7.2(%int_0) [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc4_7.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc4_7.3: ref %i32 = temporary %.loc4_7.1, %.loc4_7.2
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc4_7.3)
|
||||
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc4_7.3, constants.%DestroyOp
|
||||
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc4_7.3)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @DestroyOp(%self.param: ref %i32) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- val_form_param.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: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %.754: Core.Form = value_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.e8c: 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.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete]
|
||||
// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %x.param: %i32 = value_param call_param0
|
||||
// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.754] {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc4_15.2: Core.Form = value_form %i32 [concrete = constants.%.754]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc4_6.2: %i32 = form_binding x, %x.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%x.param: %i32);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5]
|
||||
// CHECK:STDOUT: %bound_method.loc9_5.1: <bound method> = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc9_5.2: <bound method> = bound_method %int_0, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc9_5.2(%int_0) [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc9_5.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %.loc9_5.2: %i32 = converted %int_0, %.loc9_5.1 [concrete = constants.%int_0.6a9]
|
||||
// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc9_5.2)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- type_component_needs_conversion.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
|
||||
// CHECK:STDOUT: %.9f9: Core.Form = ref_form %empty_tuple.type [concrete]
|
||||
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %.loc4_6.1: %pattern_type = form_binding_pattern x [concrete]
|
||||
// CHECK:STDOUT: %.loc4_7: %pattern_type = form_param_pattern %.loc4_6.1, call_param0 [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %x.param: ref %empty_tuple.type = ref_param call_param0
|
||||
// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.9f9] {
|
||||
// CHECK:STDOUT: %.loc4_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
|
||||
// CHECK:STDOUT: %.loc4_20.2: type = converted %.loc4_20.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
|
||||
// CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %.loc4_20.2 [concrete = constants.%.9f9]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc4_6.2: ref %empty_tuple.type = form_binding x, %x.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%x.param: ref %empty_tuple.type);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_todo_ref_return_form.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_todo_var_return_form.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_todo_val_return_form.carbon
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/none.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_todo_form_thunk.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_todo_form_thunk.carbon
|
||||
|
||||
interface I {
|
||||
let T:! type;
|
||||
// CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+7]]:8: error: semantics TODO: `Support for cloning form bindings` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F(x:? form(ref ()), y: T);
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+3]]:8: error: value expression passed to reference parameter [ValueForRefParam]
|
||||
// CHECK:STDERR: fn F(x:? form(ref ()), y: T);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
fn F(x:? form(ref ()), y: T);
|
||||
}
|
||||
|
||||
class C {
|
||||
impl as I where .T = () {
|
||||
// CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam]
|
||||
// CHECK:STDERR: fn F(x:? form(ref ()), ());
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE-8]]:3: note: while building thunk to match the signature of this function [ThunkSignature]
|
||||
// CHECK:STDERR: fn F(x:? form(ref ()), y: T);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F(x:? form(ref ()), ());
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,12 @@ static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id,
|
||||
auto entity_name = context.entity_names().Get(pattern.entity_name_id);
|
||||
CARBON_CHECK((pattern.kind == SemIR::SymbolicBindingPattern::Kind) ==
|
||||
entity_name.bind_index().has_value());
|
||||
|
||||
CARBON_CHECK((pattern.kind == SemIR::FormBindingPattern::Kind) ==
|
||||
entity_name.form_id.has_value());
|
||||
if (pattern.kind == SemIR::FormBindingPattern::Kind) {
|
||||
context.TODO(pattern_id, "Support for cloning form bindings");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
// Get the transformed type of the binding.
|
||||
if (new_pattern_type_id == SemIR::ErrorInst::TypeId) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
@@ -75,10 +80,11 @@ static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id,
|
||||
{.block_ids = {SemIR::InstBlockId::Empty}, .result_id = type_inst_id});
|
||||
|
||||
// Rebuild the binding pattern.
|
||||
return AddBindingPattern(context, SemIR::LocId(pattern_id),
|
||||
entity_name.name_id, type_id, type_expr_region_id,
|
||||
pattern.kind, entity_name.is_template,
|
||||
/*is_unused=*/false)
|
||||
return AddBindingPattern(
|
||||
context, SemIR::LocId(pattern_id), entity_name.name_id, type_id,
|
||||
/*form_id=*/SemIR::ConstantId::None, type_expr_region_id,
|
||||
pattern.kind, entity_name.is_template,
|
||||
/*is_unused=*/false)
|
||||
.pattern_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,11 @@ auto GetConstType(Context& context, SemIR::TypeInstId inner_type_id)
|
||||
return GetTypeImpl<SemIR::ConstType>(context, inner_type_id);
|
||||
}
|
||||
|
||||
auto GetTypeComponent(Context& context, SemIR::InstId form_inst_id)
|
||||
-> SemIR::TypeId {
|
||||
return GetTypeImpl<SemIR::TypeComponentOf>(context, form_inst_id);
|
||||
}
|
||||
|
||||
auto GetQualifiedType(Context& context, SemIR::TypeId type_id,
|
||||
SemIR::TypeQualifiers quals) -> SemIR::TypeId {
|
||||
if (quals.HasAnyOf(SemIR::TypeQualifiers::Const)) {
|
||||
|
||||
@@ -125,6 +125,10 @@ auto GetTupleType(Context& context, llvm::ArrayRef<SemIR::InstId> type_inst_ids)
|
||||
auto GetPatternType(Context& context, SemIR::TypeId scrutinee_type_id)
|
||||
-> SemIR::TypeId;
|
||||
|
||||
// Returns the type component of the given form value.
|
||||
auto GetTypeComponent(Context& context, SemIR::InstId form_inst_id)
|
||||
-> SemIR::TypeId;
|
||||
|
||||
// Returns an unbound element type.
|
||||
auto GetUnboundElementType(Context& context, SemIR::TypeInstId class_type_id,
|
||||
SemIR::TypeInstId element_type_id) -> SemIR::TypeId;
|
||||
|
||||
@@ -437,6 +437,7 @@ CARBON_DIAGNOSTIC_KIND(DerefOfType)
|
||||
CARBON_DIAGNOSTIC_KIND(CompileTimeBindingInVarDecl)
|
||||
CARBON_DIAGNOSTIC_KIND(CompoundMemberAccessDoesNotUseBase)
|
||||
CARBON_DIAGNOSTIC_KIND(EvalRequiresConstantValue)
|
||||
CARBON_DIAGNOSTIC_KIND(FormExprEvaluationFailure)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclPrevious)
|
||||
CARBON_DIAGNOSTIC_KIND(NameUseBeforeDecl)
|
||||
|
||||
@@ -119,6 +119,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
||||
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
||||
}
|
||||
|
||||
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::FormBinding inst) -> void {
|
||||
context.SetLocal(inst_id, context.GetValue(inst.value_id));
|
||||
}
|
||||
|
||||
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::BlockArg inst) -> void {
|
||||
context.SetLocal(
|
||||
|
||||
@@ -167,6 +167,14 @@ fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);
|
||||
fn FormParam(x:? Form);
|
||||
fn ComplexFormParam(x:? X.Y(Z));
|
||||
|
||||
// --- fail_ref_on_form_parameter.carbon
|
||||
|
||||
// CHECK:STDERR: fail_ref_on_form_parameter.carbon:[[@LINE+4]]:6: error: expected `:` binding after `ref` [ExpectedRuntimeBindingPatternAfterRef]
|
||||
// CHECK:STDERR: fn F(ref x:? Form);
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
fn F(ref x:? Form);
|
||||
|
||||
// --- return_form.carbon
|
||||
|
||||
fn SimpleReturnForm() ->? Form;
|
||||
@@ -457,6 +465,20 @@ fn ComplexReturnForm() ->? X.Y(Z);
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 13},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: - filename: fail_ref_on_form_parameter.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameMaybeBeforeSignature', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'RefBindingName', text: 'ref', has_error: yes, subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Form'},
|
||||
// CHECK:STDOUT: {kind: 'FormBindingPattern', text: ':?', has_error: yes, subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 9},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: - filename: return_form.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
|
||||
@@ -17,7 +17,7 @@ struct EntityName : public Printable<EntityName> {
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "{name: " << name_id << ", parent_scope: " << parent_scope_id
|
||||
<< ", index: " << bind_index_value << ", is_template: " << is_template
|
||||
<< ", is_unused: " << is_unused << "}";
|
||||
<< ", is_unused: " << is_unused << ", form: " << form_id << "}";
|
||||
}
|
||||
|
||||
friend auto CarbonHashtableEq(const EntityName& lhs, const EntityName& rhs)
|
||||
@@ -58,6 +58,13 @@ struct EntityName : public Printable<EntityName> {
|
||||
bool is_template : 1 = false;
|
||||
// Whether this binding is marked unused.
|
||||
bool is_unused : 1 = false;
|
||||
|
||||
// The declared form of the binding. This is guaranteed to be set for
|
||||
// `:?` bindings, and may be set for other binding kinds as well.
|
||||
//
|
||||
// TODO: Unify this with the previous three fields, which also represent form
|
||||
// information.
|
||||
ConstantId form_id = ConstantId::None;
|
||||
};
|
||||
|
||||
// Value store for EntityName. In addition to the regular ValueStore
|
||||
|
||||
@@ -91,6 +91,25 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id)
|
||||
return ExprCategory::ReprInitializing;
|
||||
}
|
||||
}
|
||||
} else if constexpr (std::same_as<TypedInstT, FormBinding>) {
|
||||
auto form_id = ir->entity_names().Get(inst.entity_name_id).form_id;
|
||||
if (form_id.is_symbolic()) {
|
||||
return ExprCategory::Dependent;
|
||||
}
|
||||
auto form_inst_id = ir->constant_values().GetInstId(form_id);
|
||||
auto form_inst = ir->insts().Get(form_inst_id);
|
||||
CARBON_KIND_SWITCH(form_inst) {
|
||||
case InitForm::Kind:
|
||||
// A `var` binding pattern produces a `ref` binding.
|
||||
case RefForm::Kind:
|
||||
return ExprCategory::DurableRef;
|
||||
case ValueForm::Kind:
|
||||
return ExprCategory::Value;
|
||||
case ErrorInst::Kind:
|
||||
return ExprCategory::Error;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected kind for form inst {0}", form_inst);
|
||||
}
|
||||
} else {
|
||||
static_assert(
|
||||
TypedInstT::Kind.expr_category().TryAsComputedCategory() !=
|
||||
|
||||
@@ -65,8 +65,8 @@ struct AnyAggregateValue {
|
||||
// Common representation for various `*binding_pattern` nodes.
|
||||
struct AnyBindingPattern {
|
||||
// TODO: Also handle TemplateBindingPattern once it exists.
|
||||
using CategoryInfo = CategoryOf<RefBindingPattern, SymbolicBindingPattern,
|
||||
ValueBindingPattern>;
|
||||
using CategoryInfo = CategoryOf<FormBindingPattern, RefBindingPattern,
|
||||
SymbolicBindingPattern, ValueBindingPattern>;
|
||||
|
||||
InstKind kind;
|
||||
|
||||
@@ -83,8 +83,8 @@ struct AnyBindingPattern {
|
||||
// Common representation for various `bind*` nodes.
|
||||
struct AnyBinding {
|
||||
// TODO: Also handle BindTemplateName once it exists.
|
||||
using CategoryInfo =
|
||||
CategoryOf<AliasBinding, RefBinding, SymbolicBinding, ValueBinding>;
|
||||
using CategoryInfo = CategoryOf<AliasBinding, FormBinding, RefBinding,
|
||||
SymbolicBinding, ValueBinding>;
|
||||
|
||||
InstKind kind;
|
||||
TypeId type_id;
|
||||
@@ -98,8 +98,8 @@ struct AnyBinding {
|
||||
// Common representation for various `bind*` nodes, and `export name`.
|
||||
struct AnyBindingOrExportDecl {
|
||||
// TODO: Also handle BindTemplateName once it exists.
|
||||
using CategoryInfo = CategoryOf<AliasBinding, RefBinding, SymbolicBinding,
|
||||
ValueBinding, ExportDecl>;
|
||||
using CategoryInfo = CategoryOf<AliasBinding, FormBinding, RefBinding,
|
||||
SymbolicBinding, ValueBinding, ExportDecl>;
|
||||
|
||||
InstKind kind;
|
||||
TypeId type_id;
|
||||
@@ -159,8 +159,9 @@ struct AnyParam {
|
||||
// A pattern that represents a `Call` parameter. It delegates to subpattern_id
|
||||
// in pattern matching.
|
||||
struct AnyParamPattern {
|
||||
using CategoryInfo = CategoryOf<OutParamPattern, RefParamPattern,
|
||||
ValueParamPattern, VarParamPattern>;
|
||||
using CategoryInfo =
|
||||
CategoryOf<FormParamPattern, OutParamPattern, RefParamPattern,
|
||||
ValueParamPattern, VarParamPattern>;
|
||||
|
||||
InstKind kind;
|
||||
|
||||
@@ -171,6 +172,21 @@ struct AnyParamPattern {
|
||||
CallParamIndex index;
|
||||
};
|
||||
|
||||
// An inst that represents a primitive form.
|
||||
struct AnyPrimitiveForm {
|
||||
using CategoryInfo = CategoryOf<InitForm, RefForm, ValueForm>;
|
||||
|
||||
InstKind kind;
|
||||
|
||||
// Always FormType.
|
||||
TypeId type_id;
|
||||
|
||||
// The type component of the form.
|
||||
TypeInstId type_component_id;
|
||||
|
||||
AnyRawId arg1;
|
||||
};
|
||||
|
||||
// A type qualifier that wraps another type and has the same object
|
||||
// representation. Qualifiers are arranged so that adding a qualifier is
|
||||
// generally safe, and removing a qualifier is not necessarily safe or correct.
|
||||
|
||||
@@ -69,6 +69,9 @@ CARBON_SEM_IR_INST_KIND(FloatLiteralType)
|
||||
CARBON_SEM_IR_INST_KIND(FloatLiteralValue)
|
||||
CARBON_SEM_IR_INST_KIND(FloatType)
|
||||
CARBON_SEM_IR_INST_KIND(FloatValue)
|
||||
CARBON_SEM_IR_INST_KIND(FormBinding)
|
||||
CARBON_SEM_IR_INST_KIND(FormBindingPattern)
|
||||
CARBON_SEM_IR_INST_KIND(FormParamPattern)
|
||||
CARBON_SEM_IR_INST_KIND(FormType)
|
||||
CARBON_SEM_IR_INST_KIND(FunctionDecl)
|
||||
CARBON_SEM_IR_INST_KIND(FunctionType)
|
||||
@@ -151,6 +154,7 @@ CARBON_SEM_IR_INST_KIND(TupleLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(TuplePattern)
|
||||
CARBON_SEM_IR_INST_KIND(TupleType)
|
||||
CARBON_SEM_IR_INST_KIND(TupleValue)
|
||||
CARBON_SEM_IR_INST_KIND(TypeComponentOf)
|
||||
CARBON_SEM_IR_INST_KIND(TypeLiteral)
|
||||
CARBON_SEM_IR_INST_KIND(TypeOfInst)
|
||||
CARBON_SEM_IR_INST_KIND(TypeType)
|
||||
@@ -160,6 +164,7 @@ CARBON_SEM_IR_INST_KIND(UninitializedValue)
|
||||
CARBON_SEM_IR_INST_KIND(ValueAsRef)
|
||||
CARBON_SEM_IR_INST_KIND(ValueBinding)
|
||||
CARBON_SEM_IR_INST_KIND(ValueBindingPattern)
|
||||
CARBON_SEM_IR_INST_KIND(ValueForm)
|
||||
CARBON_SEM_IR_INST_KIND(ValueOfInitializer)
|
||||
CARBON_SEM_IR_INST_KIND(ValueParam)
|
||||
CARBON_SEM_IR_INST_KIND(ValueParamPattern)
|
||||
|
||||
@@ -667,6 +667,11 @@ class Stringifier {
|
||||
}
|
||||
}
|
||||
|
||||
auto StringifyInst(InstId /*inst_id*/, TypeComponentOf inst) -> void {
|
||||
*out_ << "<type component of form ";
|
||||
step_stack_->Push(inst.form_inst_id, ">");
|
||||
}
|
||||
|
||||
auto StringifyInst(InstId inst_id, TypeOfInst /*inst*/) -> void {
|
||||
// Print the constant value if we've already computed the inst.
|
||||
auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id);
|
||||
|
||||
@@ -98,6 +98,7 @@ auto TypeIterator::ProcessTypeId(TypeId type_id) -> std::optional<Step> {
|
||||
case FacetType::Kind:
|
||||
case FloatLiteralType::Kind:
|
||||
case FloatType::Kind:
|
||||
case FormType::Kind:
|
||||
case FunctionType::Kind:
|
||||
case FunctionTypeWithSelfType::Kind:
|
||||
case GenericClassType::Kind:
|
||||
|
||||
@@ -746,6 +746,51 @@ struct FloatValue {
|
||||
FloatId float_id;
|
||||
};
|
||||
|
||||
// A form binding, such as the `x` declared by `x:? F`. See `AnyBinding` for
|
||||
// member documentation.
|
||||
struct FormBinding {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FormBinding.Define<Parse::FormBindingPatternId>(
|
||||
{.ir_name = "form_binding",
|
||||
.expr_category = ComputedExprCategory::DependsOnOperands,
|
||||
.constant_kind = InstConstantKind::Never});
|
||||
|
||||
TypeId type_id;
|
||||
EntityNameId entity_name_id;
|
||||
InstId value_id;
|
||||
};
|
||||
|
||||
// A form binding pattern, such as `x:? F`. See `AnyBindingPattern` for member
|
||||
// documentation.
|
||||
struct FormBindingPattern {
|
||||
static constexpr auto Kind =
|
||||
InstKind::FormBindingPattern.Define<Parse::FormBindingPatternId>(
|
||||
{.ir_name = "form_binding_pattern",
|
||||
.expr_category = ExprCategory::Pattern,
|
||||
.constant_kind = InstConstantKind::AlwaysUnique,
|
||||
.is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
// Note that the EntityName's `form_id` represents the scrutinee form, so it
|
||||
// doesn't directly correspond to `type_id` (which is a pattern type).
|
||||
EntityNameId entity_name_id;
|
||||
};
|
||||
|
||||
// A pattern that represents a form-parameterized parameter, such as `x:? F`.
|
||||
// See `AnyParamPattern` for member documentation.
|
||||
struct FormParamPattern {
|
||||
// TODO: Replace `Parse::NodeId` with `Parse::FormBindingPattern`.
|
||||
static constexpr auto Kind = InstKind::FormParamPattern.Define<Parse::NodeId>(
|
||||
{.ir_name = "form_param_pattern",
|
||||
.expr_category = ExprCategory::Pattern,
|
||||
.constant_kind = InstConstantKind::AlwaysUnique,
|
||||
.is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
InstId subpattern_id;
|
||||
CallParamIndex index;
|
||||
};
|
||||
|
||||
// The type `Core.Form`.
|
||||
struct FormType : public SingletonTypeInst<InstKind::FormType, "Core.Form"> {
|
||||
// `FormType` is always set complete in file.cpp.
|
||||
@@ -1395,7 +1440,8 @@ struct RefBindingPattern {
|
||||
|
||||
struct RefForm {
|
||||
static constexpr auto Kind =
|
||||
InstKind::RefForm.Define<Parse::PrefixOperatorRefId>(
|
||||
InstKind::RefForm.Define<Parse::NodeIdOneOf<Parse::PrefixOperatorRefId,
|
||||
Parse::RefPrimitiveFormId>>(
|
||||
{.ir_name = "ref_form",
|
||||
.constant_kind = InstConstantKind::Always,
|
||||
.is_lowered = false});
|
||||
@@ -1726,7 +1772,7 @@ struct SpliceInst {
|
||||
static constexpr auto Kind = InstKind::SpliceInst.Define<Parse::NodeId>(
|
||||
{.ir_name = "splice_inst",
|
||||
// TODO: The expression category is in general dependent on
|
||||
// instantiation. Add ExprCategory::Dependent to model this.
|
||||
// instantiation. Use ExprCategory::Dependent to model this.
|
||||
.expr_category = ExprCategory::Value});
|
||||
|
||||
TypeId type_id;
|
||||
@@ -1955,6 +2001,21 @@ struct TupleValue {
|
||||
InstBlockId elements_id;
|
||||
};
|
||||
|
||||
// Extracts the type component of `form_inst_id`, which must have type
|
||||
// `Core.Form`.
|
||||
struct TypeComponentOf {
|
||||
static constexpr auto Kind =
|
||||
InstKind::TypeComponentOf.Define<Parse::NoneNodeId>(
|
||||
{.ir_name = "type_component_of",
|
||||
.is_type = InstIsType::Always,
|
||||
.constant_kind = InstConstantKind::SymbolicOnly,
|
||||
.is_lowered = false});
|
||||
|
||||
// Always TypeType.
|
||||
TypeId type_id;
|
||||
InstId form_inst_id;
|
||||
};
|
||||
|
||||
// A type literal, such as `bool` or `type` or `i32`. The constant value of this
|
||||
// instruction will be the type value that the type literal evaluates to, which
|
||||
// is typically either a builtin type or a class defined in the prelude.
|
||||
@@ -2072,6 +2133,18 @@ struct ValueBindingPattern {
|
||||
EntityNameId entity_name_id;
|
||||
};
|
||||
|
||||
// A primitive value form.
|
||||
struct ValueForm {
|
||||
static constexpr auto Kind =
|
||||
InstKind::ValueForm.Define<Parse::ValPrimitiveFormId>(
|
||||
{.ir_name = "value_form",
|
||||
.constant_kind = InstConstantKind::Always,
|
||||
.is_lowered = false});
|
||||
|
||||
TypeId type_id;
|
||||
TypeInstId type_component_inst_id;
|
||||
};
|
||||
|
||||
// Converts an initializing expression to a value expression, in the case
|
||||
// where the initializing representation is the same as the value
|
||||
// representation.
|
||||
|
||||
Reference in New Issue
Block a user