Add support for field initializers (#7238)

In handle_let_and_var.cpp, field initializers are now handled like
regular `var` initializers, by calling `LocalPatternMatch`.

In pattern_match.cpp, `FieldDecl`s with initializers are handled by
storing a value in `SemIR::File::field_initializers()`. This is a new
map where the keys are `FieldDecl` `InstId`s and the map values are
`InstId`s representing the initializer value.

In convert.cpp, `ConvertStructToStructOrClass` now has a `get_default`
function parameter that callers can use to provide a field default.
`ConvertStructToClass` uses this to provide a default from field
initializers.
This commit is contained in:
Nicholas Bishop
2026-05-22 02:48:07 +00:00
committed by GitHub
parent 862b1c91f8
commit e6b1679552
19 changed files with 541 additions and 122 deletions
+3
View File
@@ -353,6 +353,9 @@ class Context {
auto facet_types() -> SemIR::FacetTypeInfoStore& {
return sem_ir().facet_types();
}
auto field_initializers() -> SemIR::File::FieldInitializerMap& {
return sem_ir().field_initializers();
}
auto identified_facet_types() -> SemIR::IdentifiedFacetTypeStore& {
return sem_ir().identified_facet_types();
}
+114 -55
View File
@@ -609,10 +609,10 @@ static auto ConvertPartialInitializerToNonPartial(
}
// Common implementation for ConvertStructToStruct and ConvertStructToClass.
template <typename TargetAccessInstT>
template <typename TargetAccessInstT, typename GetDefault>
static auto ConvertStructToStructOrClass(
Context& context, SemIR::StructType src_type, SemIR::StructType dest_type,
SemIR::InstId value_id, ConversionTarget target,
SemIR::InstId value_id, ConversionTarget target, GetDefault get_default,
SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId {
static_assert(std::is_same_v<SemIR::ClassElementAccess, TargetAccessInstT> ||
std::is_same_v<SemIR::StructAccess, TargetAccessInstT>);
@@ -641,28 +641,38 @@ static auto ConvertStructToStructOrClass(
value_id = MaterializeIfInitializer(context, value_id);
}
// Check that the structs are the same size.
// TODO: If not, include the name of the first source field that doesn't
// exist in the destination or vice versa in the diagnostic.
if (src_elem_fields.size() != dest_elem_fields_size) {
if (target.diagnose) {
CARBON_DIAGNOSTIC(
StructInitElementCountMismatch, Error,
"cannot initialize {0:class|struct} with {1} field{1:s} from struct "
"with {2} field{2:s}",
Diagnostics::BoolAsSelect, Diagnostics::IntAsSelect,
Diagnostics::IntAsSelect);
context.emitter().Emit(value_loc_id, StructInitElementCountMismatch,
ToClass, dest_elem_fields_size,
src_elem_fields.size());
}
return SemIR::ErrorInst::InstId;
Set<SemIR::NameId> dest_field_names;
for (auto field : dest_elem_fields) {
dest_field_names.Insert(field.name_id);
}
// Prepare to look up fields in the source by index.
// Prepare to look up fields in the source by index. Also check for
// source fields that don't match any field in the destination.
Map<SemIR::NameId, int32_t> src_field_indexes;
if (src_type.fields_id != dest_type.fields_id) {
for (auto [i, field] : llvm::enumerate(src_elem_fields)) {
if (!dest_field_names.Lookup(field.name_id)) {
if (target.diagnose) {
if (literal_elems_id.has_value()) {
CARBON_DIAGNOSTIC(StructInitUnexpectedFieldInLiteral, Error,
"struct {0} has no field named `{1}`",
SemIR::TypeId, SemIR::NameId);
context.emitter().Emit(value_loc_id,
StructInitUnexpectedFieldInLiteral,
target.type_id, field.name_id);
} else {
CARBON_DIAGNOSTIC(StructInitUnexpectedFieldInConversion, Error,
"cannot convert from struct type {0} to {1}: "
"unexpected field `{2}` in source type",
TypeOfInstId, SemIR::TypeId, SemIR::NameId);
context.emitter().Emit(value_loc_id,
StructInitUnexpectedFieldInConversion,
value_id, target.type_id, field.name_id);
}
}
return SemIR::ErrorInst::InstId;
}
auto result = src_field_indexes.Insert(field.name_id, i);
CARBON_CHECK(result.is_inserted(), "Duplicate field in source structure");
}
@@ -675,7 +685,8 @@ static auto ConvertStructToStructOrClass(
// of the source.
// TODO: Annotate diagnostics coming from here with the element index.
auto new_block =
literal_elems_id.has_value() && !dest_vptr_index.has_value()
literal_elems_id.has_value() && !dest_vptr_index.has_value() &&
literal_elems.size() == dest_elem_fields_size
? SemIR::CopyOnWriteInstBlock(&sem_ir, literal_elems_id)
: SemIR::CopyOnWriteInstBlock(
&sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{
@@ -717,33 +728,14 @@ static auto ConvertStructToStructOrClass(
// Find the matching source field.
auto src_field_index = i;
bool found = true;
if (src_type.fields_id != dest_type.fields_id) {
if (auto lookup = src_field_indexes.Lookup(dest_field.name_id)) {
src_field_index = lookup.value();
} else {
if (target.diagnose) {
if (literal_elems_id.has_value()) {
CARBON_DIAGNOSTIC(
StructInitMissingFieldInLiteral, Error,
"missing value for field `{0}` in struct initialization",
SemIR::NameId);
context.emitter().Emit(value_loc_id,
StructInitMissingFieldInLiteral,
dest_field.name_id);
} else {
CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error,
"cannot convert from struct type {0} to {1}: "
"missing field `{2}` in source type",
TypeOfInstId, SemIR::TypeId, SemIR::NameId);
context.emitter().Emit(value_loc_id,
StructInitMissingFieldInConversion, value_id,
target.type_id, dest_field.name_id);
}
}
return SemIR::ErrorInst::InstId;
found = false;
}
}
auto src_field = src_elem_fields[src_field_index];
// When initializing the `.base` field of a class, the destination type is
// `partial Base`, not `Base`.
@@ -758,23 +750,51 @@ static auto ConvertStructToStructOrClass(
dest_field_type_inst_id = context.types().GetTypeInstId(partial_type_id);
}
// TODO: This call recurses back into conversion. Switch to an iterative
// approach.
auto dest_field_index = src_field_index;
if (dest_vptr_index.has_value() &&
static_cast<int32_t>(src_field_index) >= dest_vptr_index.index) {
dest_field_index += 1;
SemIR::InstId init_id = SemIR::InstId::None;
if (found) {
auto src_field = src_elem_fields[src_field_index];
// TODO: This call recurses back into conversion. Switch to an iterative
// approach.
auto dest_field_index = src_field_index;
if (dest_vptr_index.has_value() &&
static_cast<int32_t>(src_field_index) >= dest_vptr_index.index) {
dest_field_index += 1;
}
init_id = ConvertAggregateElement<SemIR::StructAccess, TargetAccessInstT>(
context, value_loc_id, value_id, src_field.type_inst_id,
literal_elems, inner_kind, target.storage_id, dest_field_type_inst_id,
target.storage_access_block, src_field_index, dest_field_index);
} else {
init_id = get_default(dest_field.name_id);
}
auto init_id =
ConvertAggregateElement<SemIR::StructAccess, TargetAccessInstT>(
context, value_loc_id, value_id, src_field.type_inst_id,
literal_elems, inner_kind, target.storage_id,
dest_field_type_inst_id, target.storage_access_block,
src_field_index, dest_field_index);
if (init_id == SemIR::ErrorInst::InstId) {
return SemIR::ErrorInst::InstId;
}
if (!init_id.has_value()) {
if (target.diagnose) {
if (literal_elems_id.has_value()) {
CARBON_DIAGNOSTIC(
StructInitMissingFieldInLiteral, Error,
"missing value for field `{0}` in struct initialization",
SemIR::NameId);
context.emitter().Emit(value_loc_id, StructInitMissingFieldInLiteral,
dest_field.name_id);
} else {
CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error,
"cannot convert from struct type {0} to {1}: "
"missing field `{2}` in source type",
TypeOfInstId, SemIR::TypeId, SemIR::NameId);
context.emitter().Emit(value_loc_id,
StructInitMissingFieldInConversion, value_id,
target.type_id, dest_field.name_id);
}
}
return SemIR::ErrorInst::InstId;
}
// When initializing the base, adjust the type of the initializer from
// `partial Base` to `Base`. This isn't strictly correct, since we haven't
// finished initializing a `Base` until we store to the vptr, but is better
@@ -825,7 +845,8 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
SemIR::InstId value_id,
ConversionTarget target) -> SemIR::InstId {
return ConvertStructToStructOrClass<SemIR::StructAccess>(
context, src_type, dest_type, value_id, target);
context, src_type, dest_type, value_id, target,
/*get_default=*/[](SemIR::NameId) { return SemIR::InstId::None; });
}
// Performs a conversion from a struct to a class type. This function only
@@ -863,8 +884,46 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
SemIR::LocId(value_id), {.type_id = target.type_id});
}
const auto& dest_class_scope =
context.name_scopes().Get(dest_class_info.scope_id);
// Provide the default value for a field. Returns `InstId::None` if no
// default is available, or `ErrorInst::InstId` if a diagnosed error
// occurs.
auto get_default = [&](SemIR::NameId name_id) {
if (!context.insts().Is<SemIR::StructLiteral>(value_id)) {
return SemIR::InstId::None;
}
// Look up the field name in the class to get the corresponding
// `FieldDecl` `InstId`.
auto entry_id = dest_class_scope.Lookup(name_id);
if (!entry_id.has_value()) {
return SemIR::InstId::None;
}
// Look up the initializer `InstId` for the field and eval as a
// constant.
auto field_inst_id =
dest_class_scope.GetEntry(*entry_id).result.target_inst_id();
auto lookup = context.field_initializers().Lookup(field_inst_id);
if (!lookup) {
return SemIR::InstId::None;
}
auto initializer_id = lookup.value();
SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant;
const_id = GetConstantValueInSpecific(
context.sem_ir(), dest_type.specific_id, initializer_id);
if (const_id == SemIR::ConstantId::NotConstant) {
context.TODO(initializer_id, "field initializer is not constant");
return SemIR::ErrorInst::InstId;
}
return context.constant_values().GetInstId(const_id);
};
return ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
context, src_type, dest_struct_type, value_id, target,
context, src_type, dest_struct_type, value_id, target, get_default,
is_partial ? nullptr : &dest_type);
}
+7 -15
View File
@@ -126,12 +126,13 @@ auto HandleParseNode(Context& context, Parse::VariablePatternId node_id)
pattern_id = AddInst<SemIR::VarPattern>(
context, node_id,
{.type_id = type_id, .subpattern_id = subpattern_id});
break;
} else {
// For non-static class fields, a `FieldDecl` has already been
// created; do not create a var pattern.
return true;
// For non-static class fields, a `FieldDecl` was created in
// `AddBindingPattern`. Use that as the `pattern_id` so that
// it's available when handling initializers.
pattern_id = context.field_decls_stack().PeekArray().back();
}
break;
case FullPatternStack::Kind::NotInEitherParamList:
CARBON_FATAL("Unreachable");
}
@@ -209,11 +210,6 @@ auto HandleParseNode(Context& context,
auto HandleParseNode(Context& context, Parse::VariableInitializerId node_id)
-> bool {
if (InNonStaticFieldDecl(context)) {
context.TODO(node_id, "Field initializer");
return false;
}
return HandleInitializer(context, node_id);
}
@@ -298,9 +294,7 @@ static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo {
}
}
if (!in_non_static_field_decl) {
decl_info.pattern_id = context.node_stack().PopPattern();
}
decl_info.pattern_id = context.node_stack().PopPattern();
context.node_stack().PopAndDiscardSoloNodeId<IntroducerNodeKind>();
@@ -411,9 +405,7 @@ auto HandleParseNode(Context& context, Parse::VariableDeclId node_id) -> bool {
KeywordModifierSet::Returned |
KeywordModifierSet::Static);
if (!InNonStaticFieldDecl(context)) {
LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id);
}
LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id);
context.full_pattern_stack().PopFullPattern();
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Var>();
+30
View File
@@ -16,6 +16,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/control_flow.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/pattern.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/format_providers.h"
@@ -177,6 +178,8 @@ class MatchContext {
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::ExprPattern expr_pattern,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::FieldDecl field_decl,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::ReturnSlotPattern return_slot_pattern,
SemIR::InstId scrutinee_id, WorkItem entry) -> void;
auto DoPreWork(State state, SemIR::VarPattern var_pattern,
@@ -537,6 +540,29 @@ auto MatchContext::DoPostWork(State /*state*/,
SemIR::ExprPattern /*expr_pattern*/,
WorkItem /*entry*/) -> void {}
auto MatchContext::DoPreWork(State /*state*/, SemIR::FieldDecl field_decl,
SemIR::InstId scrutinee_id, WorkItem entry)
-> void {
if (!scrutinee_id.has_value()) {
return;
}
// Get the field initializer.
auto unbound_element_type = context_.insts().GetAs<SemIR::UnboundElementType>(
context_.types().GetTypeInstId(field_decl.type_id));
auto element_type = context_.types().GetTypeIdForTypeInstId(
unbound_element_type.element_type_inst_id);
auto converted_id = ConvertToValueOfType(context_, SemIR::LocId(scrutinee_id),
scrutinee_id, element_type,
/*diagnose=*/true);
if (converted_id == SemIR::ErrorInst::InstId) {
return;
}
// Store a mapping to the field's initializer.
context_.field_initializers().Insert(entry.pattern_id, converted_id);
}
auto MatchContext::DoPreWork(State state,
SemIR::ReturnSlotPattern return_slot_pattern,
SemIR::InstId scrutinee_id, WorkItem entry)
@@ -838,6 +864,10 @@ auto MatchContext::Dispatch(State state, WorkItem entry) -> void {
DoPreWork(state, expr_pattern, work.scrutinee_id, entry);
break;
}
case CARBON_KIND(SemIR::FieldDecl field_decl): {
DoPreWork(state, field_decl, work.scrutinee_id, entry);
break;
}
case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): {
DoPreWork(state, return_slot_pattern, work.scrutinee_id, entry);
break;
+1 -1
View File
@@ -51,7 +51,7 @@ library "[[@TEST_NAME]]";
choice Never {}
// TODO: Can we produce a better diagnostic?
// CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:20: error: cannot initialize class with 1 field from struct with 0 fields [StructInitElementCountMismatch]
// CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:20: error: missing value for field `discriminant` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: let never: Never = {};
// CHECK:STDERR: ^~
// CHECK:STDERR:
+3 -3
View File
@@ -16,17 +16,17 @@ class Class {
}
fn F() {
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 1 field [StructInitElementCountMismatch]
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: {.a = 1} as Class;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
{.a = 1} as Class;
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: struct `Class` has no field named `c` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: {.a = 1, .c = 2} as Class;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
// CHECK:STDERR:
{.a = 1, .c = 2} as Class;
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 3 fields [StructInitElementCountMismatch]
// CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: struct `Class` has no field named `c` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: {.a = 1, .b = 2, .c = 3} as Class;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
@@ -1,19 +0,0 @@
// 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/class/field/fail_todo_field_initializer.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon
class Class {
// CHECK:STDERR: fail_todo_field_initializer.carbon:[[@LINE+4]]:18: error: semantics TODO: `Field initializer` [SemanticsTodo]
// CHECK:STDERR: var field: i32 = 0;
// CHECK:STDERR: ^
// CHECK:STDERR:
var field: i32 = 0;
}
@@ -0,0 +1,163 @@
// 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/class/field/field_initializer.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/field_initializer.carbon
// --- field_initializer.carbon
library "[[@TEST_NAME]]";
//@dump-sem-ir-begin
class Class {
var field: i32 = 123;
}
var C: Class = {};
//@dump-sem-ir-end
// --- fail_todo_field_initializer_import.carbon
library "[[@TEST_NAME]]";
import library "field_initializer";
// CHECK:STDERR: fail_todo_field_initializer_import.carbon:[[@LINE+4]]:17: error: missing value for field `field` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: var C2: Class = {};
// CHECK:STDERR: ^~
// CHECK:STDERR:
var C2: Class = {};
// --- field_initializer_generic.carbon
library "[[@TEST_NAME]]";
interface I {
let Default:! Self;
}
impl i32 as I where .Default = 123 {}
class Class(T:! I) {
var x: T = T.Default;
}
var C: Class(i32) = {};
// --- fail_field_initializer_invalid_name.carbon
library "[[@TEST_NAME]]";
class Class {
var field: i32 = 123;
}
// CHECK:STDERR: fail_field_initializer_invalid_name.carbon:[[@LINE+4]]:16: error: struct `Class` has no field named `not_a_field` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var C: Class = {.not_a_field = 456};
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
var C: Class = {.not_a_field = 456};
// --- fail_field_initializer_wrong_type.carbon
library "[[@TEST_NAME]]";
class Class {
// CHECK:STDERR: fail_field_initializer_wrong_type.carbon:[[@LINE+7]]:20: error: cannot implicitly convert expression of type `()` to `i32` [ConversionFailure]
// CHECK:STDERR: var field: i32 = ();
// CHECK:STDERR: ^~
// CHECK:STDERR: fail_field_initializer_wrong_type.carbon:[[@LINE+4]]:20: note: type `()` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext]
// CHECK:STDERR: var field: i32 = ();
// CHECK:STDERR: ^~
// CHECK:STDERR:
var field: i32 = ();
}
// --- fail_field_initializer_not_constant.carbon
library "[[@TEST_NAME]]";
var x: i32;
class Class {
// CHECK:STDERR: fail_field_initializer_not_constant.carbon:[[@LINE+4]]:20: error: semantics TODO: `field initializer is not constant` [SemanticsTodo]
// CHECK:STDERR: var field: i32 = x;
// CHECK:STDERR: ^
// CHECK:STDERR:
var field: i32 = x;
}
var C: Class = {};
// CHECK:STDOUT: --- field_initializer.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Class: type = class_type @Class [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete]
// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete]
// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic]
// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: 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.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %int_123.5a3: %i32 = int_value 123 [concrete]
// CHECK:STDOUT: %struct_type.field: type = struct_type {.field: %i32} [concrete]
// CHECK:STDOUT: %complete_type.97b: <witness> = complete_type_witness %struct_type.field [concrete]
// CHECK:STDOUT: %pattern_type.dd7: type = pattern_type %Class [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: %Class.val: %Class = struct_value (%int_123.5a3) [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = 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.ec3)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {}
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %C.patt: %pattern_type.dd7 = ref_binding_pattern C [concrete]
// CHECK:STDOUT: %C.var_patt: %pattern_type.dd7 = var_pattern %C.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.var: ref %Class = var %C.var_patt [concrete]
// CHECK:STDOUT: %Class.ref: type = name_ref Class, %Class.decl [concrete = constants.%Class]
// CHECK:STDOUT: %C: ref %Class = ref_binding C, %C.var [concrete = %C.var]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: %.loc5_12: %Class.elem = field_decl field, element0 [concrete]
// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff]
// CHECK:STDOUT: %impl.elem0: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b]
// CHECK:STDOUT: %bound_method.loc5_20.1: <bound method> = bound_method %int_123, %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.loc5_20.2: <bound method> = bound_method %int_123, %specific_fn [concrete = constants.%bound_method]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_20.2(%int_123) [concrete = constants.%int_123.5a3]
// CHECK:STDOUT: %.loc5_20.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.5a3]
// CHECK:STDOUT: %.loc5_20.2: %i32 = converted %int_123, %.loc5_20.1 [concrete = constants.%int_123.5a3]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%struct_type.field [concrete = constants.%complete_type.97b]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class
// CHECK:STDOUT: .field = %.loc5_12
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc8_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
// CHECK:STDOUT: %.loc8_17.2: init %Class to file.%C.var = class_init (constants.%int_123.5a3) [concrete = constants.%Class.val]
// CHECK:STDOUT: %.loc8_1: init %Class = converted %.loc8_17.1, %.loc8_17.2 [concrete = constants.%Class.val]
// CHECK:STDOUT: assign file.%C.var, %.loc8_1
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
+2 -2
View File
@@ -398,7 +398,7 @@ library "[[@TEST_NAME]]";
interface K {
let V:! type;
// CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+3]]:20: error: cannot convert from struct type `{.a: ()}` to `{.x: ()}`: missing field `x` in source type [StructInitMissingFieldInConversion]
// CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+3]]:20: error: cannot convert from struct type `{.a: ()}` to `{.x: ()}`: unexpected field `a` in source type [StructInitUnexpectedFieldInConversion]
// CHECK:STDERR: fn F[self: Self](v: V) -> V;
// CHECK:STDERR: ^~~~
fn F[self: Self](v: V) -> V;
@@ -412,7 +412,7 @@ impl () as K where .V = {.a: ()} {
// CHECK:STDERR: fn F[self: Self](v: V) -> V;
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+7]]:3: error: cannot convert from struct type `{.x: ()}` to `{.a: ()}`: missing field `a` in source type [StructInitMissingFieldInConversion]
// CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE+7]]:3: error: cannot convert from struct type `{.x: ()}` to `{.a: ()}`: unexpected field `x` in source type [StructInitUnexpectedFieldInConversion]
// CHECK:STDERR: fn F[unused self: Self](v: {.x: ()}) -> {.x: ()} { return v; }
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_associated_type_in_signature_mismatch.carbon:[[@LINE-14]]:3: note: while building thunk to match the signature of this function [ThunkSignature]
+1 -1
View File
@@ -12,7 +12,7 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_empty.carbon
// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+4]]:20: error: cannot initialize struct with 1 field from struct with 0 fields [StructInitElementCountMismatch]
// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: var x: {.a: i32} = {};
// CHECK:STDERR: ^~
// CHECK:STDERR:
+1 -1
View File
@@ -12,7 +12,7 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_nested.carbon
// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+4]]:19: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+4]]:19: error: struct `{.a: {}}` has no field named `b` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var x: {.a: {}} = {.b = {}};
// CHECK:STDERR: ^~~~~~~~~
// CHECK:STDERR:
@@ -12,7 +12,7 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_assign_to_empty.carbon
// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+4]]:13: error: cannot initialize struct with 0 fields from struct with 1 field [StructInitElementCountMismatch]
// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+4]]:13: error: struct `{}` has no field named `a` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var x: {} = {.a = 1};
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
@@ -12,13 +12,20 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_field_name_mismatch.carbon
// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral]
var a: {.a: i32} = {.a = 1 };
// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:29: error: cannot convert from struct type `{.a: i32}` to `{.a: i32, .b: i32}`: missing field `b` in source type [StructInitMissingFieldInConversion]
// CHECK:STDERR: var b: {.a: i32, .b: i32} = a;
// CHECK:STDERR: ^
// CHECK:STDERR:
var b: {.a: i32, .b: i32} = a;
// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: struct `{.a: i32}` has no field named `b` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var x: {.a: i32} = {.b = 1};
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
var x: {.a: i32} = {.b = 1};
// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: cannot convert from struct type `{.a: i32}` to `{.b: i32}`: missing field `b` in source type [StructInitMissingFieldInConversion]
// CHECK:STDERR: fail_field_name_mismatch.carbon:[[@LINE+4]]:20: error: cannot convert from struct type `{.a: i32}` to `{.b: i32}`: unexpected field `a` in source type [StructInitUnexpectedFieldInConversion]
// CHECK:STDERR: var y: {.b: i32} = x;
// CHECK:STDERR: ^
// CHECK:STDERR:
@@ -30,12 +37,46 @@ var y: {.b: i32} = x;
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete]
// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete]
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete]
// CHECK:STDOUT: %pattern_type.79c: type = pattern_type %struct_type.a [concrete]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %struct_type.a.72d: type = struct_type {.a: %i32} [concrete]
// CHECK:STDOUT: %pattern_type.79c: type = pattern_type %struct_type.a.72d [concrete]
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %struct_type.a.a6c: type = struct_type {.a: Core.IntLiteral} [concrete]
// CHECK:STDOUT: %struct.48c: %struct_type.a.a6c = struct_value (%int_1.5b8) [concrete]
// CHECK:STDOUT: %.17c: ref %i32 = struct_access file.%a.var, element0 [concrete]
// CHECK:STDOUT: %ImplicitAs.type.649: type = generic_interface_type @ImplicitAs [concrete]
// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.649 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic]
// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: 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.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %int_1.d5e: %i32 = int_value 1 [concrete]
// CHECK:STDOUT: %struct.096: %struct_type.a.72d = struct_value (%int_1.d5e) [concrete]
// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete]
// CHECK:STDOUT: %pattern_type.32c: type = pattern_type %struct_type.a.b [concrete]
// CHECK:STDOUT: %.568: ref %i32 = struct_access file.%b.var, element0 [concrete]
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.b5d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.c85: %Int.as.Copy.impl.Op.type.b5d = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.impl_witness.32d: <witness> = impl_witness imports.%Copy.impl_witness_table.07a, @Int.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.4a0: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.c4a: %Int.as.Copy.impl.Op.type.4a0 = struct_value () [concrete]
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.32d) [concrete]
// CHECK:STDOUT: %Copy.WithSelf.Op.type.afe: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
// CHECK:STDOUT: %.a5f: type = fn_type_with_self_type %Copy.WithSelf.Op.type.afe, %Copy.facet [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.c4a, @Int.as.Copy.impl.Op(%int_32) [concrete]
// CHECK:STDOUT: %struct_type.b.a15: type = struct_type {.b: Core.IntLiteral} [concrete]
// CHECK:STDOUT: %struct: %struct_type.b.a15 = struct_value (%int_1) [concrete]
// CHECK:STDOUT: %struct.62b: %struct_type.b.a15 = struct_value (%int_1.5b8) [concrete]
// CHECK:STDOUT: %struct_type.b.177: type = struct_type {.b: %i32} [concrete]
// CHECK:STDOUT: %pattern_type.3b8: type = pattern_type %struct_type.b.177 [concrete]
// CHECK:STDOUT: }
@@ -43,36 +84,67 @@ var y: {.b: i32} = x;
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: .Int = %Core.Int
// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs
// CHECK:STDOUT: .Copy = %Core.Copy
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic]
// CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.649 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = 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.ec3)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type]
// CHECK:STDOUT: %Core.import_ref.809: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.b5d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.c85)]
// CHECK:STDOUT: %Copy.impl_witness_table.07a = impl_witness_table (%Core.import_ref.809), @Int.as.Copy.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .a = %a
// CHECK:STDOUT: .b = %b
// CHECK:STDOUT: .x = %x
// CHECK:STDOUT: .y = %y
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %a.patt: %pattern_type.79c = ref_binding_pattern a [concrete]
// CHECK:STDOUT: %a.var_patt: %pattern_type.79c = var_pattern %a.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %a.var: ref %struct_type.a.72d = var %a.var_patt [concrete]
// CHECK:STDOUT: %.loc15: type = splice_block %struct_type.a.loc15 [concrete = constants.%struct_type.a.72d] {
// CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.a.loc15: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.72d]
// CHECK:STDOUT: }
// CHECK:STDOUT: %a: ref %struct_type.a.72d = ref_binding a, %a.var [concrete = %a.var]
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %b.patt: %pattern_type.32c = ref_binding_pattern b [concrete]
// CHECK:STDOUT: %b.var_patt: %pattern_type.32c = var_pattern %b.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %b.var: ref %struct_type.a.b = var %b.var_patt [concrete]
// CHECK:STDOUT: %.loc20: type = splice_block %struct_type.a.b [concrete = constants.%struct_type.a.b] {
// CHECK:STDOUT: %i32.loc20_13: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %i32.loc20_22: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
// CHECK:STDOUT: }
// CHECK:STDOUT: %b: ref %struct_type.a.b = ref_binding b, %b.var [concrete = %b.var]
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %x.patt: %pattern_type.79c = ref_binding_pattern x [concrete]
// CHECK:STDOUT: %x.var_patt: %pattern_type.79c = var_pattern %x.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %x.var: ref %struct_type.a = var %x.var_patt [concrete]
// CHECK:STDOUT: %.loc19: type = splice_block %struct_type.a [concrete = constants.%struct_type.a] {
// CHECK:STDOUT: %i32.loc19: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
// CHECK:STDOUT: %x.var: ref %struct_type.a.72d = var %x.var_patt [concrete]
// CHECK:STDOUT: %.loc26: type = splice_block %struct_type.a.loc26 [concrete = constants.%struct_type.a.72d] {
// CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.a.loc26: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.72d]
// CHECK:STDOUT: }
// CHECK:STDOUT: %x: ref %struct_type.a = ref_binding x, %x.var [concrete = %x.var]
// CHECK:STDOUT: %x: ref %struct_type.a.72d = ref_binding x, %x.var [concrete = %x.var]
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %y.patt: %pattern_type.3b8 = ref_binding_pattern y [concrete]
// CHECK:STDOUT: %y.var_patt: %pattern_type.3b8 = var_pattern %y.patt [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT: %y.var: ref %struct_type.b.177 = var %y.var_patt [concrete]
// CHECK:STDOUT: %.loc25: type = splice_block %struct_type.b [concrete = constants.%struct_type.b.177] {
// CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %.loc32: type = splice_block %struct_type.b [concrete = constants.%struct_type.b.177] {
// CHECK:STDOUT: %i32.loc32: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %i32} [concrete = constants.%struct_type.b.177]
// CHECK:STDOUT: }
// CHECK:STDOUT: %y: ref %struct_type.b.177 = ref_binding y, %y.var [concrete = %y.var]
@@ -80,10 +152,32 @@ var y: {.b: i32} = x;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
// CHECK:STDOUT: %.loc19: %struct_type.b.a15 = struct_literal (%int_1) [concrete = constants.%struct]
// CHECK:STDOUT: %int_1.loc15: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
// CHECK:STDOUT: %.loc15_28.1: %struct_type.a.a6c = struct_literal (%int_1.loc15) [concrete = constants.%struct.48c]
// CHECK:STDOUT: %impl.elem0.loc15: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b]
// CHECK:STDOUT: %bound_method.loc15_28.1: <bound method> = bound_method %int_1.loc15, %impl.elem0.loc15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
// CHECK:STDOUT: %specific_fn.loc15: <specific function> = specific_function %impl.elem0.loc15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
// CHECK:STDOUT: %bound_method.loc15_28.2: <bound method> = bound_method %int_1.loc15, %specific_fn.loc15 [concrete = constants.%bound_method]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc15_28.2(%int_1.loc15) [concrete = constants.%int_1.d5e]
// CHECK:STDOUT: %.loc15_28.2: init %i32 = converted %int_1.loc15, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.d5e]
// CHECK:STDOUT: %.loc15_28.3: init %struct_type.a.72d to file.%a.var = struct_init (%.loc15_28.2) [concrete = constants.%struct.096]
// CHECK:STDOUT: %.loc15_1: init %struct_type.a.72d = converted %.loc15_28.1, %.loc15_28.3 [concrete = constants.%struct.096]
// CHECK:STDOUT: assign file.%a.var, %.loc15_1
// CHECK:STDOUT: %a.ref: ref %struct_type.a.72d = name_ref a, file.%a [concrete = file.%a.var]
// CHECK:STDOUT: %.loc20_29.1: ref %i32 = struct_access %a.ref, element0 [concrete = constants.%.17c]
// CHECK:STDOUT: %.loc20_29.2: %i32 = acquire_value %.loc20_29.1
// CHECK:STDOUT: %impl.elem0.loc20: %.a5f = impl_witness_access constants.%Copy.impl_witness.32d, element0 [concrete = constants.%Int.as.Copy.impl.Op.c4a]
// CHECK:STDOUT: %bound_method.loc20_29.1: <bound method> = bound_method %.loc20_29.2, %impl.elem0.loc20
// CHECK:STDOUT: %specific_fn.loc20: <specific function> = specific_function %impl.elem0.loc20, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
// CHECK:STDOUT: %bound_method.loc20_29.2: <bound method> = bound_method %.loc20_29.2, %specific_fn.loc20
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc20_29.2(%.loc20_29.2)
// CHECK:STDOUT: %.loc20_29.3: ref %i32 = struct_access file.%b.var, element0 [concrete = constants.%.568]
// CHECK:STDOUT: %.loc20_29.4: init %i32 to %.loc20_29.3 = in_place_init %Int.as.Copy.impl.Op.call
// CHECK:STDOUT: assign file.%b.var, <error>
// CHECK:STDOUT: %int_1.loc26: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
// CHECK:STDOUT: %.loc26: %struct_type.b.a15 = struct_literal (%int_1.loc26) [concrete = constants.%struct.62b]
// CHECK:STDOUT: assign file.%x.var, <error>
// CHECK:STDOUT: %x.ref: ref %struct_type.a = name_ref x, file.%x [concrete = file.%x.var]
// CHECK:STDOUT: %x.ref: ref %struct_type.a.72d = name_ref x, file.%x [concrete = file.%x.var]
// CHECK:STDOUT: assign file.%y.var, <error>
// CHECK:STDOUT: return
// CHECK:STDOUT: }
@@ -12,7 +12,7 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_field_type_mismatch.carbon
// CHECK:STDERR: fail_field_type_mismatch.carbon:[[@LINE+4]]:20: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: fail_field_type_mismatch.carbon:[[@LINE+4]]:20: error: struct `{.a: i32}` has no field named `b` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var x: {.a: i32} = {.b = 1.0};
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
+34 -5
View File
@@ -12,7 +12,7 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/struct/fail_too_few_values.carbon
// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+4]]:29: error: cannot initialize struct with 2 fields from struct with 1 field [StructInitElementCountMismatch]
// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+4]]:29: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: var x: {.a: i32, .b: i32} = {.a = 1};
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
@@ -27,18 +27,39 @@ var x: {.a: i32, .b: i32} = {.a = 1};
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete]
// CHECK:STDOUT: %pattern_type.32c: type = pattern_type %struct_type.a.b [concrete]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: Core.IntLiteral} [concrete]
// CHECK:STDOUT: %struct: %struct_type.a = struct_value (%int_1) [concrete]
// CHECK:STDOUT: %struct: %struct_type.a = struct_value (%int_1.5b8) [concrete]
// CHECK:STDOUT: %.2e9: ref %i32 = struct_access file.%x.var, element0 [concrete]
// CHECK:STDOUT: %ImplicitAs.type.649: type = generic_interface_type @ImplicitAs [concrete]
// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.649 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.type.544: 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.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ec3: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [symbolic]
// CHECK:STDOUT: %ImplicitAs.impl_witness.fb6: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.1a5, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7: 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.e6b: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.ef7 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.544 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.fb6) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.367: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
// CHECK:STDOUT: %.205: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.367, %ImplicitAs.facet [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b [concrete]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %int_1.d5e: %i32 = int_value 1 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: .Int = %Core.Int
// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic]
// CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.649 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
// CHECK:STDOUT: %Core.import_ref.dd3: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766) = 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.ec3)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1a5 = impl_witness_table (%Core.import_ref.dd3), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -62,8 +83,16 @@ var x: {.a: i32, .b: i32} = {.a = 1};
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
// CHECK:STDOUT: %.loc19: %struct_type.a = struct_literal (%int_1) [concrete = constants.%struct]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
// CHECK:STDOUT: %.loc19_36.1: %struct_type.a = struct_literal (%int_1) [concrete = constants.%struct]
// CHECK:STDOUT: %impl.elem0: %.205 = impl_witness_access constants.%ImplicitAs.impl_witness.fb6, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e6b]
// CHECK:STDOUT: %bound_method.loc19_36.1: <bound method> = bound_method %int_1, %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.loc19_36.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc19_36.2(%int_1) [concrete = constants.%int_1.d5e]
// CHECK:STDOUT: %.loc19_36.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.d5e]
// CHECK:STDOUT: %.loc19_36.3: ref %i32 = struct_access file.%x.var, element0 [concrete = constants.%.2e9]
// CHECK:STDOUT: %.loc19_36.4: init %i32 to %.loc19_36.3 = in_place_init %.loc19_36.2 [concrete = constants.%int_1.d5e]
// CHECK:STDOUT: assign file.%x.var, <error>
// CHECK:STDOUT: return
// CHECK:STDOUT: }
+1 -1
View File
@@ -34,7 +34,7 @@ var c: C({.a = 1, .b = 2}) = F();
// --- fail_bad_type.impl.carbon
impl package Implicit;
// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: missing value for field `a` in struct initialization [StructInitMissingFieldInLiteral]
// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: struct `{.a: i32, .b: i32}` has no field named `c` [StructInitUnexpectedFieldInLiteral]
// CHECK:STDERR: var c_bad: C({.c = 1, .d = 2}) = F();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-4]]:1: in import [InImport]
+2 -1
View File
@@ -482,9 +482,10 @@ CARBON_DIAGNOSTIC_KIND(StringAtIndexNegative)
CARBON_DIAGNOSTIC_KIND(StringLiteralTooLong)
CARBON_DIAGNOSTIC_KIND(StringLiteralTypeIncomplete)
CARBON_DIAGNOSTIC_KIND(StringLiteralTypeUnexpected)
CARBON_DIAGNOSTIC_KIND(StructInitElementCountMismatch)
CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInLiteral)
CARBON_DIAGNOSTIC_KIND(StructInitMissingFieldInConversion)
CARBON_DIAGNOSTIC_KIND(StructInitUnexpectedFieldInLiteral)
CARBON_DIAGNOSTIC_KIND(StructInitUnexpectedFieldInConversion)
CARBON_DIAGNOSTIC_KIND(StructNameDuplicate)
CARBON_DIAGNOSTIC_KIND(StructNamePrevious)
CARBON_DIAGNOSTIC_KIND(TupleIndexNotConstant)
+53
View File
@@ -0,0 +1,53 @@
// 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/lower/testdata/class/field_initializer.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/class/field_initializer.carbon
// --- field_initializer.carbon
library "[[@TEST_NAME]]";
class C {
var a: i32 = 123;
}
var c: C = {};
// CHECK:STDOUT: ; ModuleID = 'field_initializer.carbon'
// CHECK:STDOUT: source_filename = "field_initializer.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: @_Cc.Main = global { i32 } zeroinitializer
// CHECK:STDOUT: @C.val.loc7_1 = internal constant { i32 } { i32 123 }
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_Cc.Main, ptr align 4 @C.val.loc7_1, i64 4, i1 false), !dbg !7
// CHECK:STDOUT: ret void, !dbg !8
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
// CHECK:STDOUT:
// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !3 = !DIFile(filename: "field_initializer.carbon", directory: "")
// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !3, type: !5, spFlags: DISPFlagDefinition, unit: !2)
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 1, scope: !4)
// CHECK:STDOUT: !8 = !DILocation(line: 0, scope: !4)
+14
View File
@@ -6,6 +6,7 @@
#define CARBON_TOOLCHAIN_SEM_IR_FILE_H_
#include "common/error.h"
#include "common/map.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Allocator.h"
@@ -206,6 +207,12 @@ class File : public Printable<File> {
// TODO: Rename these to `facet_type_infos`.
auto facet_types() -> FacetTypeInfoStore& { return facet_types_; }
auto facet_types() const -> const FacetTypeInfoStore& { return facet_types_; }
using FieldInitializerMap = Map<SemIR::InstId, SemIR::InstId>;
auto field_initializers() -> FieldInitializerMap& {
return field_initializers_;
}
auto identified_facet_types() -> IdentifiedFacetTypeStore& {
return identified_facet_types_;
}
@@ -349,6 +356,13 @@ class File : public Printable<File> {
// Storage for classes.
ClassStore classes_;
// Map containing initializers for class fields. The map keys are
// `InstId`s corresponding to `FielDecl`s.
//
// TODO: consider replacing this map with a separate store for fields
// and tracking a new `FieldId` in the `FieldDecl`.
FieldInitializerMap field_initializers_;
// Storage for interfaces.
InterfaceStore interfaces_;