mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
Constant evaluation for aggregate access. (#3625)
Also, form `addr_of error` instead of `addr_of operand` when `operand` is not a reference expression, so that we don't try to constant-evaluate a meaningless expression. Also, expect a constant for an array bound rather than specifically an integer literal. This allows constant evaluation results to be more easily tested by inspecting array bounds.
This commit is contained in:
+101
-13
@@ -65,6 +65,12 @@ static auto MakeConstantResult(Context& context, SemIR::Inst inst, Phase phase)
|
||||
}
|
||||
}
|
||||
|
||||
// Forms a `constant_id` describing why an evaluation was not constant.
|
||||
static auto MakeNonConstantResult(Phase phase) -> SemIR::ConstantId {
|
||||
return phase == Phase::UnknownDueToError ? SemIR::ConstantId::Error
|
||||
: SemIR::ConstantId::NotConstant;
|
||||
}
|
||||
|
||||
// `GetConstantValue` checks to see whether the provided ID describes a value
|
||||
// with constant phase, and if so, returns the corresponding constant value.
|
||||
// Overloads are provided for different kinds of ID.
|
||||
@@ -120,22 +126,36 @@ static auto ReplaceFieldWithConstantValue(Context& context, InstT* inst,
|
||||
|
||||
// If the specified fields of the given typed instruction have constant values,
|
||||
// replaces the fields with their constant values and builds a corresponding
|
||||
// constant value. Otherwise returns `SemIR::InstId::Invalid`.
|
||||
template <typename InstT, typename... EachFieldIdT>
|
||||
static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst,
|
||||
EachFieldIdT InstT::*... each_field_id)
|
||||
-> SemIR::ConstantId {
|
||||
// constant value. The constant value is then checked by calling
|
||||
// `validate_fn(typed_inst)`, which should return a `bool` indicating whether
|
||||
// the new constant is valid. If validation passes, a corresponding ConstantId
|
||||
// for the new constant is returned. Otherwise returns
|
||||
// `ConstantId::NotConstant`. Returns `ConstantId::Error` if any subexpression
|
||||
// is an error.
|
||||
template <typename InstT, typename ValidateFn, typename... EachFieldIdT>
|
||||
static auto RebuildAndValidateIfFieldsAreConstant(
|
||||
Context& context, SemIR::Inst inst, ValidateFn validate_fn,
|
||||
EachFieldIdT InstT::*... each_field_id) -> SemIR::ConstantId {
|
||||
// Build a constant instruction by replacing each non-constant operand with
|
||||
// its constant value.
|
||||
auto typed_inst = inst.As<InstT>();
|
||||
Phase phase = Phase::Template;
|
||||
if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id,
|
||||
&phase) &&
|
||||
...)) {
|
||||
...) &&
|
||||
validate_fn(typed_inst)) {
|
||||
return MakeConstantResult(context, typed_inst, phase);
|
||||
}
|
||||
return phase == Phase::UnknownDueToError ? SemIR::ConstantId::Error
|
||||
: SemIR::ConstantId::NotConstant;
|
||||
return MakeNonConstantResult(phase);
|
||||
}
|
||||
|
||||
// Same as above but with no validation step.
|
||||
template <typename InstT, typename... EachFieldIdT>
|
||||
static auto RebuildIfFieldsAreConstant(Context& context, SemIR::Inst inst,
|
||||
EachFieldIdT InstT::*... each_field_id)
|
||||
-> SemIR::ConstantId {
|
||||
return RebuildAndValidateIfFieldsAreConstant(
|
||||
context, inst, [](...) { return true; }, each_field_id...);
|
||||
}
|
||||
|
||||
// Rebuilds the given aggregate initialization instruction as a corresponding
|
||||
@@ -154,6 +174,63 @@ static auto RebuildInitAsValue(Context& context, SemIR::Inst inst,
|
||||
phase);
|
||||
}
|
||||
|
||||
// Performs an access into an aggregate, retrieving the specified element.
|
||||
static auto PerformAggregateAccess(Context& context, SemIR::Inst inst)
|
||||
-> SemIR::ConstantId {
|
||||
auto access_inst = inst.As<SemIR::AnyAggregateAccess>();
|
||||
Phase phase = Phase::Template;
|
||||
if (auto aggregate_id =
|
||||
GetConstantValue(context, access_inst.aggregate_id, &phase);
|
||||
aggregate_id.is_valid()) {
|
||||
if (auto aggregate =
|
||||
context.insts().TryGetAs<SemIR::AnyAggregateValue>(aggregate_id)) {
|
||||
auto elements = context.inst_blocks().Get(aggregate->elements_id);
|
||||
auto index = static_cast<size_t>(access_inst.index.index);
|
||||
CARBON_CHECK(index < elements.size()) << "Access out of bounds.";
|
||||
// `Phase` is not used here. If this element is a template constant, then
|
||||
// so is the result of indexing, even if the aggregate also contains a
|
||||
// symbolic context.
|
||||
return context.constant_values().Get(elements[index]);
|
||||
} else {
|
||||
CARBON_CHECK(phase != Phase::Template)
|
||||
<< "Failed to evaluate template constant " << inst;
|
||||
}
|
||||
}
|
||||
return MakeNonConstantResult(phase);
|
||||
}
|
||||
|
||||
// Performs an index into a homogeneous aggregate, retrieving the specified
|
||||
// element.
|
||||
static auto PerformAggregateIndex(Context& context, SemIR::Inst inst)
|
||||
-> SemIR::ConstantId {
|
||||
auto index_inst = inst.As<SemIR::AnyAggregateIndex>();
|
||||
Phase phase = Phase::Template;
|
||||
auto aggregate_id =
|
||||
GetConstantValue(context, index_inst.aggregate_id, &phase);
|
||||
auto index_id = GetConstantValue(context, index_inst.index_id, &phase);
|
||||
if (aggregate_id.is_valid() && index_id.is_valid()) {
|
||||
auto aggregate =
|
||||
context.insts().TryGetAs<SemIR::AnyAggregateValue>(aggregate_id);
|
||||
auto index = context.insts().TryGetAs<SemIR::IntLiteral>(index_id);
|
||||
if (aggregate && index) {
|
||||
const auto& index_val = context.ints().Get(index->int_id);
|
||||
auto elements = context.inst_blocks().Get(aggregate->elements_id);
|
||||
if (index_val.ult(elements.size())) {
|
||||
return context.constant_values().Get(
|
||||
elements[index_val.getZExtValue()]);
|
||||
} else {
|
||||
// TODO: In this case, this instruction is a constant, but it indexes an
|
||||
// array with a statically out-of-bounds index. This should produce an
|
||||
// error rather than just treating the instruction as non-constant.
|
||||
}
|
||||
} else {
|
||||
CARBON_CHECK(phase != Phase::Template)
|
||||
<< "Failed to evaluate template constant " << inst;
|
||||
}
|
||||
}
|
||||
return MakeNonConstantResult(phase);
|
||||
}
|
||||
|
||||
auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
|
||||
-> SemIR::ConstantId {
|
||||
// TODO: Ensure we have test coverage for each of these cases that can result
|
||||
@@ -167,8 +244,18 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
|
||||
return RebuildIfFieldsAreConstant(context, inst,
|
||||
&SemIR::AddrOf::lvalue_id);
|
||||
case SemIR::ArrayType::Kind:
|
||||
return RebuildIfFieldsAreConstant(context, inst,
|
||||
&SemIR::ArrayType::bound_id);
|
||||
return RebuildAndValidateIfFieldsAreConstant(
|
||||
context, inst,
|
||||
[&](SemIR::ArrayType result) {
|
||||
// TODO: If the bound doesn't fit in 64 bits or is negative,
|
||||
// produce an error rather than a non-constant type.
|
||||
// TODO: Support a symbolic bound here. This will require fixing
|
||||
// callers of `GetArrayBoundValue`.
|
||||
auto int_bound =
|
||||
context.insts().TryGetAs<SemIR::IntLiteral>(result.bound_id);
|
||||
return context.ints().Get(int_bound->int_id).getActiveBits() <= 64;
|
||||
},
|
||||
&SemIR::ArrayType::bound_id);
|
||||
case SemIR::BoundMethod::Kind:
|
||||
return RebuildIfFieldsAreConstant(context, inst,
|
||||
&SemIR::BoundMethod::object_id,
|
||||
@@ -227,13 +314,14 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
|
||||
// by `APInt`s with different bit widths.
|
||||
return MakeConstantResult(context, inst, Phase::Template);
|
||||
|
||||
// TODO: Support subobject access.
|
||||
case SemIR::ArrayIndex::Kind:
|
||||
// The elements of a constant aggregate can be accessed.
|
||||
case SemIR::ClassElementAccess::Kind:
|
||||
case SemIR::StructAccess::Kind:
|
||||
case SemIR::TupleAccess::Kind:
|
||||
return PerformAggregateAccess(context, inst);
|
||||
case SemIR::ArrayIndex::Kind:
|
||||
case SemIR::TupleIndex::Kind:
|
||||
break;
|
||||
return PerformAggregateIndex(context, inst);
|
||||
|
||||
// TODO: These need special handling.
|
||||
case SemIR::BindValue::Kind:
|
||||
|
||||
@@ -32,21 +32,25 @@ auto HandleArrayExpr(Context& context, Parse::ArrayExprId parse_node) -> bool {
|
||||
context.node_stack()
|
||||
.PopAndDiscardSoloParseNode<Parse::NodeKind::ArrayExprSemi>();
|
||||
auto element_type_inst_id = context.node_stack().PopExpr();
|
||||
auto bound_inst = context.insts().Get(bound_inst_id);
|
||||
if (auto literal = bound_inst.TryAs<SemIR::IntLiteral>()) {
|
||||
const auto& bound_value = context.ints().Get(literal->int_id);
|
||||
// TODO: Produce an error if the array type is too large.
|
||||
if (bound_value.getActiveBits() <= 64) {
|
||||
context.AddInstAndPush(
|
||||
{parse_node, SemIR::ArrayType{SemIR::TypeId::TypeType, bound_inst_id,
|
||||
ExprAsType(context, parse_node,
|
||||
element_type_inst_id)}});
|
||||
return true;
|
||||
}
|
||||
|
||||
// The array bound must be a constant.
|
||||
//
|
||||
// TODO: Should we support runtime-phase bounds in cases such as:
|
||||
// comptime fn F(n: i32) -> type { return [i32; n]; }
|
||||
auto bound_inst = context.constant_values().Get(bound_inst_id);
|
||||
if (!bound_inst.is_constant()) {
|
||||
CARBON_DIAGNOSTIC(InvalidArrayExpr, Error,
|
||||
"Array bound is not a constant.");
|
||||
context.emitter().Emit(context.insts().GetParseNode(bound_inst_id),
|
||||
InvalidArrayExpr);
|
||||
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
|
||||
return true;
|
||||
}
|
||||
CARBON_DIAGNOSTIC(InvalidArrayExpr, Error, "Invalid array expression.");
|
||||
context.emitter().Emit(parse_node, InvalidArrayExpr);
|
||||
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
|
||||
|
||||
context.AddInstAndPush(
|
||||
{parse_node, SemIR::ArrayType{
|
||||
SemIR::TypeId::TypeType, bound_inst_id,
|
||||
ExprAsType(context, parse_node, element_type_inst_id)}});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ auto HandlePostfixOperatorStar(Context& context,
|
||||
auto HandlePrefixOperatorAmp(Context& context,
|
||||
Parse::PrefixOperatorAmpId parse_node) -> bool {
|
||||
auto value_id = context.node_stack().PopExpr();
|
||||
auto type_id = context.insts().Get(value_id).type_id();
|
||||
// Only durable reference expressions can have their address taken.
|
||||
switch (SemIR::GetExprCategory(context.sem_ir(), value_id)) {
|
||||
case SemIR::ExprCategory::DurableRef:
|
||||
@@ -215,17 +216,17 @@ auto HandlePrefixOperatorAmp(Context& context,
|
||||
CARBON_DIAGNOSTIC(AddrOfEphemeralRef, Error,
|
||||
"Cannot take the address of a temporary object.");
|
||||
context.emitter().Emit(TokenOnly(parse_node), AddrOfEphemeralRef);
|
||||
value_id = SemIR::InstId::BuiltinError;
|
||||
break;
|
||||
default:
|
||||
CARBON_DIAGNOSTIC(AddrOfNonRef, Error,
|
||||
"Cannot take the address of non-reference expression.");
|
||||
context.emitter().Emit(TokenOnly(parse_node), AddrOfNonRef);
|
||||
value_id = SemIR::InstId::BuiltinError;
|
||||
break;
|
||||
}
|
||||
context.AddInstAndPush(
|
||||
{parse_node, SemIR::AddrOf{context.GetPointerType(
|
||||
context.insts().Get(value_id).type_id()),
|
||||
value_id}});
|
||||
{parse_node, SemIR::AddrOf{context.GetPointerType(type_id), value_id}});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+21
-8
@@ -4,23 +4,36 @@
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Invalid array expression.
|
||||
// CHECK:STDERR: var a: [1; 39999999999999999993];
|
||||
// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Cannot evaluate type expression.
|
||||
// CHECK:STDERR: var a: [i32; 39999999999999999993];
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var a: [i32; 39999999999999999993];
|
||||
|
||||
// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+6]]:8: ERROR: Cannot implicitly convert from `i32` to `type`.
|
||||
// CHECK:STDERR: var b: [1; 39999999999999999993];
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var a: [1; 39999999999999999993];
|
||||
// CHECK:STDERR: fail_bound_overflow.carbon:[[@LINE+3]]:8: ERROR: Cannot evaluate type expression.
|
||||
// CHECK:STDERR: var b: [1; 39999999999999999993];
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var b: [1; 39999999999999999993];
|
||||
|
||||
// CHECK:STDOUT: --- fail_bound_overflow.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.2: i32 = int_literal 39999999999999999993 [template]
|
||||
// CHECK:STDOUT: %.1: i32 = int_literal 39999999999999999993 [template]
|
||||
// CHECK:STDOUT: %.2: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace package, {.a = %a} [template]
|
||||
// CHECK:STDOUT: %.loc10_9: i32 = int_literal 1 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc10_12: i32 = int_literal 39999999999999999993 [template = constants.%.2]
|
||||
// CHECK:STDOUT: package: <namespace> = namespace package, {.a = %a, .b = %b} [template]
|
||||
// CHECK:STDOUT: %.loc10_14: i32 = int_literal 39999999999999999993 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc10_34: type = array_type %.loc10_14, i32
|
||||
// CHECK:STDOUT: %a.var: ref <error> = var a
|
||||
// CHECK:STDOUT: %a: ref <error> = bind_name a, %a.var
|
||||
// CHECK:STDOUT: %.loc18_9: i32 = int_literal 1 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %.loc18_12: i32 = int_literal 39999999999999999993 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc18_32: type = array_type %.loc18_12, <error>
|
||||
// CHECK:STDOUT: %b.var: ref <error> = var b
|
||||
// CHECK:STDOUT: %b: ref <error> = bind_name b, %b.var
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
var tuple_copy: (i32, i32) = (1, 2) as (i32, i32);
|
||||
|
||||
var struct_copy: {.a: i32, .b: i32, .c: i32} = {.c = 3, .b = 2, .a = 1} as {.b: i32, .a: i32, .c: i32};
|
||||
|
||||
var tuple_index: [i32; 1] = (0,) as [i32; (5, 7, 1, 9)[2]];
|
||||
|
||||
var struct_access: [i32; 1] = (0,) as [i32; {.a = 3, .b = 1}.b];
|
||||
|
||||
// CHECK:STDOUT: --- aggregate.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: type = tuple_type (type, type) [template]
|
||||
// CHECK:STDOUT: %.2: type = tuple_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.3: type = ptr_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.4: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: (i32, i32) = tuple_value (%.4, %.5) [template]
|
||||
// CHECK:STDOUT: %.7: type = struct_type {.a: i32, .b: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %.8: type = ptr_type {.a: i32, .b: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %.9: i32 = int_literal 3 [template]
|
||||
// CHECK:STDOUT: %.10: type = struct_type {.c: i32, .b: i32, .a: i32} [template]
|
||||
// CHECK:STDOUT: %.11: type = struct_type {.b: i32, .a: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %.12: type = ptr_type {.b: i32, .a: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %.13: {.b: i32, .a: i32, .c: i32} = struct_value (%.5, %.4, %.9) [template]
|
||||
// CHECK:STDOUT: %.14: {.a: i32, .b: i32, .c: i32} = struct_value (%.4, %.5, %.9) [template]
|
||||
// CHECK:STDOUT: %.15: type = array_type %.4, i32 [template]
|
||||
// CHECK:STDOUT: %.16: type = ptr_type [i32; 1] [template]
|
||||
// CHECK:STDOUT: %.17: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.18: type = tuple_type (i32) [template]
|
||||
// CHECK:STDOUT: %.19: i32 = int_literal 5 [template]
|
||||
// CHECK:STDOUT: %.20: i32 = int_literal 7 [template]
|
||||
// CHECK:STDOUT: %.21: i32 = int_literal 9 [template]
|
||||
// CHECK:STDOUT: %.22: type = tuple_type (i32, i32, i32, i32) [template]
|
||||
// CHECK:STDOUT: %.23: type = ptr_type (i32, i32, i32, i32) [template]
|
||||
// CHECK:STDOUT: %.24: (i32, i32, i32, i32) = tuple_value (%.19, %.20, %.4, %.21) [template]
|
||||
// CHECK:STDOUT: %.25: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.26: [i32; 1] = tuple_value (%.17) [template]
|
||||
// CHECK:STDOUT: %.27: type = struct_type {.a: i32, .b: i32} [template]
|
||||
// CHECK:STDOUT: %.28: type = ptr_type {.a: i32, .b: i32} [template]
|
||||
// CHECK:STDOUT: %.29: {.a: i32, .b: i32} = struct_value (%.9, %.4) [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace package, {.tuple_copy = %tuple_copy, .struct_copy = %struct_copy, .tuple_index = %tuple_index, .struct_access = %struct_access} [template]
|
||||
// CHECK:STDOUT: %.loc7_26.1: (type, type) = tuple_literal (i32, i32)
|
||||
// CHECK:STDOUT: %.loc7_26.2: type = converted %.loc7_26.1, constants.%.2 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %tuple_copy.var: ref (i32, i32) = var tuple_copy
|
||||
// CHECK:STDOUT: %tuple_copy: ref (i32, i32) = bind_name tuple_copy, %tuple_copy.var
|
||||
// CHECK:STDOUT: %.loc7_31: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc7_34: i32 = int_literal 2 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc7_35.1: (i32, i32) = tuple_literal (%.loc7_31, %.loc7_34)
|
||||
// CHECK:STDOUT: %.loc7_49.1: (type, type) = tuple_literal (i32, i32)
|
||||
// CHECK:STDOUT: %.loc7_49.2: type = converted %.loc7_49.1, constants.%.2 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %.loc7_35.2: ref i32 = tuple_access %tuple_copy.var, element0
|
||||
// CHECK:STDOUT: %.loc7_35.3: init i32 = initialize_from %.loc7_31 to %.loc7_35.2 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc7_35.4: ref i32 = tuple_access %tuple_copy.var, element1
|
||||
// CHECK:STDOUT: %.loc7_35.5: init i32 = initialize_from %.loc7_34 to %.loc7_35.4 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc7_35.6: init (i32, i32) = tuple_init (%.loc7_35.3, %.loc7_35.5) to %tuple_copy.var [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc7_35.7: init (i32, i32) = converted %.loc7_35.1, %.loc7_35.6 [template = constants.%.6]
|
||||
// CHECK:STDOUT: assign %tuple_copy.var, %.loc7_35.7
|
||||
// CHECK:STDOUT: %.loc9_44: type = struct_type {.a: i32, .b: i32, .c: i32} [template = constants.%.7]
|
||||
// CHECK:STDOUT: %struct_copy.var: ref {.a: i32, .b: i32, .c: i32} = var struct_copy
|
||||
// CHECK:STDOUT: %struct_copy: ref {.a: i32, .b: i32, .c: i32} = bind_name struct_copy, %struct_copy.var
|
||||
// CHECK:STDOUT: %.loc9_54: i32 = int_literal 3 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_62: i32 = int_literal 2 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc9_70: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc9_71.1: {.c: i32, .b: i32, .a: i32} = struct_literal (%.loc9_54, %.loc9_62, %.loc9_70)
|
||||
// CHECK:STDOUT: %.loc9_102: type = struct_type {.b: i32, .a: i32, .c: i32} [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc9_71.2: {.b: i32, .a: i32, .c: i32} = struct_value (%.loc9_62, %.loc9_70, %.loc9_54) [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc9_71.3: {.b: i32, .a: i32, .c: i32} = converted %.loc9_71.1, %.loc9_71.2 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc9_71.4: i32 = struct_access %.loc9_71.3, element1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc9_71.5: ref i32 = struct_access %struct_copy.var, element1
|
||||
// CHECK:STDOUT: %.loc9_71.6: init i32 = initialize_from %.loc9_71.4 to %.loc9_71.5 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc9_71.7: i32 = struct_access %.loc9_71.3, element0 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc9_71.8: ref i32 = struct_access %struct_copy.var, element0
|
||||
// CHECK:STDOUT: %.loc9_71.9: init i32 = initialize_from %.loc9_71.7 to %.loc9_71.8 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc9_71.10: i32 = struct_access %.loc9_71.3, element2 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_71.11: ref i32 = struct_access %struct_copy.var, element2
|
||||
// CHECK:STDOUT: %.loc9_71.12: init i32 = initialize_from %.loc9_71.10 to %.loc9_71.11 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_71.13: init {.a: i32, .b: i32, .c: i32} = struct_init (%.loc9_71.6, %.loc9_71.9, %.loc9_71.12) to %struct_copy.var [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc9_71.14: init {.a: i32, .b: i32, .c: i32} = converted %.loc9_71.3, %.loc9_71.13 [template = constants.%.14]
|
||||
// CHECK:STDOUT: assign %struct_copy.var, %.loc9_71.14
|
||||
// CHECK:STDOUT: %.loc11_24: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_25: type = array_type %.loc11_24, i32 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %tuple_index.var: ref [i32; 1] = var tuple_index
|
||||
// CHECK:STDOUT: %tuple_index: ref [i32; 1] = bind_name tuple_index, %tuple_index.var
|
||||
// CHECK:STDOUT: %.loc11_30: i32 = int_literal 0 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc11_32.1: (i32,) = tuple_literal (%.loc11_30)
|
||||
// CHECK:STDOUT: %.loc11_44: i32 = int_literal 5 [template = constants.%.19]
|
||||
// CHECK:STDOUT: %.loc11_47: i32 = int_literal 7 [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc11_50: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_53: i32 = int_literal 9 [template = constants.%.21]
|
||||
// CHECK:STDOUT: %.loc11_54.1: (i32, i32, i32, i32) = tuple_literal (%.loc11_44, %.loc11_47, %.loc11_50, %.loc11_53)
|
||||
// CHECK:STDOUT: %.loc11_56: i32 = int_literal 2 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc11_54.2: (i32, i32, i32, i32) = tuple_value (%.loc11_44, %.loc11_47, %.loc11_50, %.loc11_53) [template = constants.%.24]
|
||||
// CHECK:STDOUT: %.loc11_54.3: (i32, i32, i32, i32) = converted %.loc11_54.1, %.loc11_54.2 [template = constants.%.24]
|
||||
// CHECK:STDOUT: %.loc11_57: i32 = tuple_index %.loc11_54.3, %.loc11_56 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_58: type = array_type %.loc11_57, i32 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc11_5: ref [i32; 1] = splice_block %tuple_index.var {}
|
||||
// CHECK:STDOUT: %.loc11_32.2: i32 = int_literal 0 [template = constants.%.25]
|
||||
// CHECK:STDOUT: %.loc11_32.3: ref i32 = array_index %.loc11_5, %.loc11_32.2
|
||||
// CHECK:STDOUT: %.loc11_32.4: init i32 = initialize_from %.loc11_30 to %.loc11_32.3 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc11_32.5: init [i32; 1] = array_init (%.loc11_32.4) to %.loc11_5 [template = constants.%.26]
|
||||
// CHECK:STDOUT: %.loc11_32.6: init [i32; 1] = converted %.loc11_32.1, %.loc11_32.5 [template = constants.%.26]
|
||||
// CHECK:STDOUT: assign %tuple_index.var, %.loc11_32.6
|
||||
// CHECK:STDOUT: %.loc13_26: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc13_27: type = array_type %.loc13_26, i32 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %struct_access.var: ref [i32; 1] = var struct_access
|
||||
// CHECK:STDOUT: %struct_access: ref [i32; 1] = bind_name struct_access, %struct_access.var
|
||||
// CHECK:STDOUT: %.loc13_32: i32 = int_literal 0 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_34.1: (i32,) = tuple_literal (%.loc13_32)
|
||||
// CHECK:STDOUT: %.loc13_51: i32 = int_literal 3 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_59: i32 = int_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc13_60.1: {.a: i32, .b: i32} = struct_literal (%.loc13_51, %.loc13_59)
|
||||
// CHECK:STDOUT: %.loc13_60.2: {.a: i32, .b: i32} = struct_value (%.loc13_51, %.loc13_59) [template = constants.%.29]
|
||||
// CHECK:STDOUT: %.loc13_60.3: {.a: i32, .b: i32} = converted %.loc13_60.1, %.loc13_60.2 [template = constants.%.29]
|
||||
// CHECK:STDOUT: %.loc13_61: i32 = struct_access %.loc13_60.3, element1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc13_63: type = array_type %.loc13_61, i32 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc13_5: ref [i32; 1] = splice_block %struct_access.var {}
|
||||
// CHECK:STDOUT: %.loc13_34.2: i32 = int_literal 0 [template = constants.%.25]
|
||||
// CHECK:STDOUT: %.loc13_34.3: ref i32 = array_index %.loc13_5, %.loc13_34.2
|
||||
// CHECK:STDOUT: %.loc13_34.4: init i32 = initialize_from %.loc13_32 to %.loc13_34.3 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_34.5: init [i32; 1] = array_init (%.loc13_34.4) to %.loc13_5 [template = constants.%.26]
|
||||
// CHECK:STDOUT: %.loc13_34.6: init [i32; 1] = converted %.loc13_34.1, %.loc13_34.5 [template = constants.%.26]
|
||||
// CHECK:STDOUT: assign %struct_access.var, %.loc13_34.6
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
// TODO: This creates an array temporary, which we don't yet support evaluating.
|
||||
|
||||
// CHECK:STDERR: fail_aggregate.carbon:[[@LINE+3]]:43: ERROR: Array bound is not a constant.
|
||||
// CHECK:STDERR: var array_index: [i32; 1] = (0,) as [i32; ((5, 7, 1, 9) as [i32; 4])[2]];
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
var array_index: [i32; 1] = (0,) as [i32; ((5, 7, 1, 9) as [i32; 4])[2]];
|
||||
|
||||
// CHECK:STDOUT: --- fail_aggregate.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.2: type = array_type %.1, i32 [template]
|
||||
// CHECK:STDOUT: %.3: type = ptr_type [i32; 1] [template]
|
||||
// CHECK:STDOUT: %.4: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.5: type = tuple_type (i32) [template]
|
||||
// CHECK:STDOUT: %.6: i32 = int_literal 5 [template]
|
||||
// CHECK:STDOUT: %.7: i32 = int_literal 7 [template]
|
||||
// CHECK:STDOUT: %.8: i32 = int_literal 9 [template]
|
||||
// CHECK:STDOUT: %.9: type = tuple_type (i32, i32, i32, i32) [template]
|
||||
// CHECK:STDOUT: %.10: i32 = int_literal 4 [template]
|
||||
// CHECK:STDOUT: %.11: type = array_type %.10, i32 [template]
|
||||
// CHECK:STDOUT: %.12: type = ptr_type [i32; 4] [template]
|
||||
// CHECK:STDOUT: %.13: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.14: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.15: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: %.16: i32 = int_literal 3 [template]
|
||||
// CHECK:STDOUT: %.17: [i32; 4] = tuple_value (%.6, %.7, %.1, %.8) [template]
|
||||
// CHECK:STDOUT: %.18: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace package, {.array_index = %array_index} [template]
|
||||
// CHECK:STDOUT: %.loc12_24: i32 = int_literal 1 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc12_25: type = array_type %.loc12_24, i32 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %array_index.var: ref [i32; 1] = var array_index
|
||||
// CHECK:STDOUT: %array_index: ref [i32; 1] = bind_name array_index, %array_index.var
|
||||
// CHECK:STDOUT: %.loc12_30: i32 = int_literal 0 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc12_32: (i32,) = tuple_literal (%.loc12_30)
|
||||
// CHECK:STDOUT: %.loc12_45: i32 = int_literal 5 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_48: i32 = int_literal 7 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc12_51: i32 = int_literal 1 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc12_54: i32 = int_literal 9 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc12_55.1: (i32, i32, i32, i32) = tuple_literal (%.loc12_45, %.loc12_48, %.loc12_51, %.loc12_54)
|
||||
// CHECK:STDOUT: %.loc12_66: i32 = int_literal 4 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc12_67: type = array_type %.loc12_66, i32 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc12_55.2: ref [i32; 4] = temporary_storage
|
||||
// CHECK:STDOUT: %.loc12_55.3: i32 = int_literal 0 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_55.4: ref i32 = array_index %.loc12_55.2, %.loc12_55.3
|
||||
// CHECK:STDOUT: %.loc12_55.5: init i32 = initialize_from %.loc12_45 to %.loc12_55.4 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_55.6: i32 = int_literal 1 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc12_55.7: ref i32 = array_index %.loc12_55.2, %.loc12_55.6
|
||||
// CHECK:STDOUT: %.loc12_55.8: init i32 = initialize_from %.loc12_48 to %.loc12_55.7 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc12_55.9: i32 = int_literal 2 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc12_55.10: ref i32 = array_index %.loc12_55.2, %.loc12_55.9
|
||||
// CHECK:STDOUT: %.loc12_55.11: init i32 = initialize_from %.loc12_51 to %.loc12_55.10 [template = constants.%.1]
|
||||
// CHECK:STDOUT: %.loc12_55.12: i32 = int_literal 3 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %.loc12_55.13: ref i32 = array_index %.loc12_55.2, %.loc12_55.12
|
||||
// CHECK:STDOUT: %.loc12_55.14: init i32 = initialize_from %.loc12_54 to %.loc12_55.13 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc12_55.15: init [i32; 4] = array_init (%.loc12_55.5, %.loc12_55.8, %.loc12_55.11, %.loc12_55.14) to %.loc12_55.2 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc12_55.16: init [i32; 4] = converted %.loc12_55.1, %.loc12_55.15 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc12_70: i32 = int_literal 2 [template = constants.%.18]
|
||||
// CHECK:STDOUT: %.loc12_55.17: ref [i32; 4] = temporary %.loc12_55.2, %.loc12_55.16
|
||||
// CHECK:STDOUT: %.loc12_71.1: ref i32 = array_index %.loc12_55.17, %.loc12_70
|
||||
// CHECK:STDOUT: %.loc12_71.2: i32 = bind_value %.loc12_71.1
|
||||
// CHECK:STDOUT: assign %array_index.var, <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -58,7 +58,7 @@ fn G(b: [i32; 3]) {
|
||||
// CHECK:STDOUT: %.loc14_22.1: ref [i32; 3] = value_as_ref %b.ref.loc14
|
||||
// CHECK:STDOUT: %.loc14_22.2: ref i32 = array_index %.loc14_22.1, %.loc14_21
|
||||
// CHECK:STDOUT: %.loc14_22.3: i32 = bind_value %.loc14_22.2
|
||||
// CHECK:STDOUT: %.loc14_18: i32* = addr_of %.loc14_22.3
|
||||
// CHECK:STDOUT: %.loc14_18: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: assign %pb.var, %.loc14_18
|
||||
// CHECK:STDOUT: %b.ref.loc18: [i32; 3] = name_ref b, %b
|
||||
// CHECK:STDOUT: %.loc18_5: i32 = int_literal 0 [template = constants.%.5]
|
||||
@@ -77,7 +77,7 @@ fn G(b: [i32; 3]) {
|
||||
// CHECK:STDOUT: %.loc25_20.3: ref [i32; 3] = temporary %.loc25_20.1, %.loc25_20.2
|
||||
// CHECK:STDOUT: %.loc25_24.1: ref i32 = array_index %.loc25_20.3, %.loc25_23
|
||||
// CHECK:STDOUT: %.loc25_24.2: i32 = bind_value %.loc25_24.1
|
||||
// CHECK:STDOUT: %.loc25_18: i32* = addr_of %.loc25_24.2
|
||||
// CHECK:STDOUT: %.loc25_18: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: assign %pf.var, %.loc25_18
|
||||
// CHECK:STDOUT: %F.ref.loc29: <function> = name_ref F, file.%F [template = file.%F]
|
||||
// CHECK:STDOUT: %.loc29_4.1: ref [i32; 3] = temporary_storage
|
||||
|
||||
@@ -36,7 +36,7 @@ fn Test() {
|
||||
// CHECK:STDOUT: %.loc11: <error>* = addr_of %undeclared.ref.loc11 [template = <error>]
|
||||
// CHECK:STDOUT: %undeclared.ref.loc18: <error> = name_ref undeclared, <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc18_5: <error>* = addr_of %undeclared.ref.loc18 [template = <error>]
|
||||
// CHECK:STDOUT: %.loc18_3: <error>** = addr_of %.loc18_5 [template = <error>]
|
||||
// CHECK:STDOUT: %.loc18_3: <error>** = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+49
-56
@@ -88,30 +88,23 @@ fn AddressOfParam(param: i32) {
|
||||
// CHECK:STDOUT: %.1: type = struct_type {.a: i32} [template]
|
||||
// CHECK:STDOUT: %.2: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
|
||||
// CHECK:STDOUT: %.4: i32* = addr_of %.2 [template]
|
||||
// CHECK:STDOUT: %.5: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.6: type = ptr_type bool [template]
|
||||
// CHECK:STDOUT: %.7: bool* = addr_of %.5 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = real_literal 10e-1 [template]
|
||||
// CHECK:STDOUT: %.9: type = ptr_type f64 [template]
|
||||
// CHECK:STDOUT: %.10: f64* = addr_of %.8 [template]
|
||||
// CHECK:STDOUT: %.11: type = ptr_type String [template]
|
||||
// CHECK:STDOUT: %.12: String = string_literal "Hello" [template]
|
||||
// CHECK:STDOUT: %.13: String* = addr_of %.12 [template]
|
||||
// CHECK:STDOUT: %.14: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.15: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: %.16: type = tuple_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.17: type = ptr_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.18: i32 = int_literal 5 [template]
|
||||
// CHECK:STDOUT: %.19: type = ptr_type {.a: i32} [template]
|
||||
// CHECK:STDOUT: %.20: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.21: bool* = addr_of %.20 [template]
|
||||
// CHECK:STDOUT: %.22: type = ptr_type type [template]
|
||||
// CHECK:STDOUT: %.23: type* = addr_of i32 [template]
|
||||
// CHECK:STDOUT: %.24: type = const_type i32 [template]
|
||||
// CHECK:STDOUT: %.25: type = ptr_type const i32 [template]
|
||||
// CHECK:STDOUT: %.26: type* = addr_of %.25 [template]
|
||||
// CHECK:STDOUT: %.27: (i32, i32) = tuple_value (%.14, %.15) [template]
|
||||
// CHECK:STDOUT: %.4: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.5: type = ptr_type bool [template]
|
||||
// CHECK:STDOUT: %.6: f64 = real_literal 10e-1 [template]
|
||||
// CHECK:STDOUT: %.7: type = ptr_type f64 [template]
|
||||
// CHECK:STDOUT: %.8: type = ptr_type String [template]
|
||||
// CHECK:STDOUT: %.9: String = string_literal "Hello" [template]
|
||||
// CHECK:STDOUT: %.10: i32 = int_literal 1 [template]
|
||||
// CHECK:STDOUT: %.11: i32 = int_literal 2 [template]
|
||||
// CHECK:STDOUT: %.12: type = tuple_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.13: type = ptr_type (i32, i32) [template]
|
||||
// CHECK:STDOUT: %.14: i32 = int_literal 5 [template]
|
||||
// CHECK:STDOUT: %.15: type = ptr_type {.a: i32} [template]
|
||||
// CHECK:STDOUT: %.16: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.17: type = ptr_type type [template]
|
||||
// CHECK:STDOUT: %.18: type = const_type i32 [template]
|
||||
// CHECK:STDOUT: %.19: type = ptr_type const i32 [template]
|
||||
// CHECK:STDOUT: %.20: (i32, i32) = tuple_value (%.10, %.11) [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
@@ -133,45 +126,45 @@ fn AddressOfParam(param: i32) {
|
||||
// CHECK:STDOUT: fn @AddressOfLiteral() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc15_4: i32 = int_literal 0 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %.loc15_3: i32* = addr_of %.loc15_4 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc19_4: bool = bool_literal true [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc19_3: bool* = addr_of %.loc19_4 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc23_4: f64 = real_literal 10e-1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc23_3: f64* = addr_of %.loc23_4 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc27_4: String = string_literal "Hello" [template = constants.%.12]
|
||||
// CHECK:STDOUT: %.loc27_3: String* = addr_of %.loc27_4 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc31_5: i32 = int_literal 1 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc31_8: i32 = int_literal 2 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc15_3: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc19_4: bool = bool_literal true [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc19_3: bool* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc23_4: f64 = real_literal 10e-1 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc23_3: f64* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc27_4: String = string_literal "Hello" [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc27_3: String* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc31_5: i32 = int_literal 1 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc31_8: i32 = int_literal 2 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc31_9: (i32, i32) = tuple_literal (%.loc31_5, %.loc31_8)
|
||||
// CHECK:STDOUT: %.loc31_3: (i32, i32)* = addr_of %.loc31_9
|
||||
// CHECK:STDOUT: %.loc35_10: i32 = int_literal 5 [template = constants.%.18]
|
||||
// CHECK:STDOUT: %.loc31_3: (i32, i32)* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc35_10: i32 = int_literal 5 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc35_11: {.a: i32} = struct_literal (%.loc35_10)
|
||||
// CHECK:STDOUT: %.loc35_3: {.a: i32}* = addr_of %.loc35_11
|
||||
// CHECK:STDOUT: %.loc35_3: {.a: i32}* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AddressOfOperator() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc42_5: bool = bool_literal true [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc42_10.1: bool = bool_literal false [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc42_5: bool = bool_literal true [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc42_10.1: bool = bool_literal false [template = constants.%.16]
|
||||
// CHECK:STDOUT: if %.loc42_5 br !and.rhs else br !and.result(%.loc42_10.1)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !and.rhs:
|
||||
// CHECK:STDOUT: %.loc42_14: bool = bool_literal false [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc42_14: bool = bool_literal false [template = constants.%.16]
|
||||
// CHECK:STDOUT: br !and.result(%.loc42_14)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !and.result:
|
||||
// CHECK:STDOUT: %.loc42_10.2: bool = block_arg !and.result
|
||||
// CHECK:STDOUT: %.loc42_3: bool* = addr_of %.loc42_10.2
|
||||
// CHECK:STDOUT: %.loc42_3: bool* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %H.ref: <function> = name_ref H, file.%H [template = file.%H]
|
||||
// CHECK:STDOUT: %.loc46_5.1: init {.a: i32} = call %H.ref()
|
||||
// CHECK:STDOUT: %.loc46_5.2: ref {.a: i32} = temporary_storage
|
||||
// CHECK:STDOUT: %.loc46_5.3: ref {.a: i32} = temporary %.loc46_5.2, %.loc46_5.1
|
||||
// CHECK:STDOUT: %.loc46_7: ref i32 = struct_access %.loc46_5.3, element0
|
||||
// CHECK:STDOUT: %.loc46_3: i32* = addr_of %.loc46_7
|
||||
// CHECK:STDOUT: %.loc50_9: bool = bool_literal true [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc50_5: bool = not %.loc50_9 [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc50_3: bool* = addr_of %.loc50_5 [template = constants.%.21]
|
||||
// CHECK:STDOUT: %.loc46_3: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc50_9: bool = bool_literal true [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc50_5: bool = not %.loc50_9 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %.loc50_3: bool* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -179,29 +172,29 @@ fn AddressOfParam(param: i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %G.ref: <function> = name_ref G, file.%G [template = file.%G]
|
||||
// CHECK:STDOUT: %.loc57_5: init i32 = call %G.ref()
|
||||
// CHECK:STDOUT: %.loc57_3: i32* = addr_of %.loc57_5
|
||||
// CHECK:STDOUT: %.loc57_3: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AddressOfType() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc64: type* = addr_of i32 [template = constants.%.23]
|
||||
// CHECK:STDOUT: %.loc68_5: type = const_type i32 [template = constants.%.24]
|
||||
// CHECK:STDOUT: %.loc68_14: type = ptr_type const i32 [template = constants.%.25]
|
||||
// CHECK:STDOUT: %.loc68_3: type* = addr_of %.loc68_14 [template = constants.%.26]
|
||||
// CHECK:STDOUT: %.loc64: type* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: %.loc68_5: type = const_type i32 [template = constants.%.18]
|
||||
// CHECK:STDOUT: %.loc68_14: type = ptr_type const i32 [template = constants.%.19]
|
||||
// CHECK:STDOUT: %.loc68_3: type* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AddressOfTupleElementValue() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc75_6: i32 = int_literal 1 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc75_9: i32 = int_literal 2 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc75_6: i32 = int_literal 1 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc75_9: i32 = int_literal 2 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc75_10.1: (i32, i32) = tuple_literal (%.loc75_6, %.loc75_9)
|
||||
// CHECK:STDOUT: %.loc75_12: i32 = int_literal 0 [template = constants.%.2]
|
||||
// CHECK:STDOUT: %.loc75_10.2: (i32, i32) = tuple_value (%.loc75_6, %.loc75_9) [template = constants.%.27]
|
||||
// CHECK:STDOUT: %.loc75_10.3: (i32, i32) = converted %.loc75_10.1, %.loc75_10.2 [template = constants.%.27]
|
||||
// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10.3, %.loc75_12
|
||||
// CHECK:STDOUT: %.loc75_3: i32* = addr_of %.loc75_13
|
||||
// CHECK:STDOUT: %.loc75_10.2: (i32, i32) = tuple_value (%.loc75_6, %.loc75_9) [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc75_10.3: (i32, i32) = converted %.loc75_10.1, %.loc75_10.2 [template = constants.%.20]
|
||||
// CHECK:STDOUT: %.loc75_13: i32 = tuple_index %.loc75_10.3, %.loc75_12 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc75_3: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -211,7 +204,7 @@ fn AddressOfParam(param: i32) {
|
||||
// CHECK:STDOUT: %param_addr.var: ref i32* = var param_addr
|
||||
// CHECK:STDOUT: %param_addr: ref i32* = bind_name param_addr, %param_addr.var
|
||||
// CHECK:STDOUT: %param.ref: i32 = name_ref param, %param
|
||||
// CHECK:STDOUT: %.loc82_26: i32* = addr_of %param.ref
|
||||
// CHECK:STDOUT: %.loc82_26: i32* = addr_of <error> [template = <error>]
|
||||
// CHECK:STDOUT: assign %param_addr.var, %.loc82_26
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
@@ -73,6 +73,31 @@ struct ArrayIndex {
|
||||
InstId index_id;
|
||||
};
|
||||
|
||||
// Common representation for aggregate access nodes, which access a fixed
|
||||
// element of an aggregate.
|
||||
struct AnyAggregateAccess {
|
||||
static constexpr InstKind Kinds[] = {InstKind::StructAccess,
|
||||
InstKind::TupleAccess,
|
||||
InstKind::ClassElementAccess};
|
||||
|
||||
InstKind kind;
|
||||
TypeId type_id;
|
||||
InstId aggregate_id;
|
||||
ElementIndex index;
|
||||
};
|
||||
|
||||
// Common representation for aggregate index nodes, which access an element
|
||||
// determined by evaluating an expression.
|
||||
struct AnyAggregateIndex {
|
||||
static constexpr InstKind Kinds[] = {InstKind::ArrayIndex,
|
||||
InstKind::TupleIndex};
|
||||
|
||||
InstKind kind;
|
||||
TypeId type_id;
|
||||
InstId aggregate_id;
|
||||
InstId index_id;
|
||||
};
|
||||
|
||||
// Common representation for all kinds of aggregate initialization.
|
||||
struct AnyAggregateInit {
|
||||
static constexpr InstKind Kinds[] = {InstKind::ArrayInit, InstKind::ClassInit,
|
||||
|
||||
Reference in New Issue
Block a user