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:
Geoff Romer
2026-02-26 23:01:24 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins Carbon Infra Bot
parent f9ab963bd6
commit bf9219d30e
33 changed files with 1115 additions and 313 deletions
+9
View File
@@ -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
View File
@@ -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,
+17 -3
View File
@@ -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.
+5 -7
View File
@@ -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
+12
View File
@@ -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(
+1 -1
View File
@@ -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);
+77 -24
View File
@@ -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,
+39 -14
View File
@@ -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
+1 -1
View File
@@ -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);
+2
View File
@@ -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: {
+21 -8
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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]}
@@ -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
View File
@@ -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
View File
@@ -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 ()), ());
}
}
+11 -5
View File
@@ -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;
}
+5
View File
@@ -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)) {
+4
View File
@@ -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;
+1
View File
@@ -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)
+5
View File
@@ -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(
+22
View File
@@ -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: ''},
+8 -1
View File
@@ -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
+19
View File
@@ -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() !=
+24 -8
View File
@@ -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.
+5
View File
@@ -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)
+5
View File
@@ -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);
+1
View File
@@ -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:
+75 -2
View File
@@ -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.