Skip to content

Commit

Permalink
Add support for converting between integer types (#4753)
Browse files Browse the repository at this point in the history
Add a builtin `"int.convert"` supporting unchecked conversions between
different integer types. This performs a truncation, zero-extension, or
sign-extension, depending on the widths of the operands and the
signedness of the source type. Add explicit `As` support to the prelude.
No implicit conversions are supported yet as we don't have a way to
express the constraint that we can only implicitly convert to wider
types.
  • Loading branch information
zygoloid authored Jan 8, 2025
1 parent 96d836f commit 246ec78
Show file tree
Hide file tree
Showing 14 changed files with 545 additions and 98 deletions.
5 changes: 5 additions & 0 deletions core/prelude/types/int.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ impl forall [N:! IntLiteral()] Int(N) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

// TODO: Allow as an implicit conversion if N > M.
impl forall [M:! IntLiteral(), N:! IntLiteral()] Int(M) as As(Int(N)) {
fn Convert[self: Self]() -> Int(N) = "int.convert";
}

// Comparisons.

impl forall [N:! IntLiteral()] Int(N) as Eq {
Expand Down
16 changes: 16 additions & 0 deletions core/prelude/types/uint.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package Core library "prelude/types/uint";

import library "prelude/types/int_literal";
import library "prelude/types/int";
import library "prelude/operators";

private fn MakeUInt(size: IntLiteral()) -> type = "int.make_type_unsigned";
Expand Down Expand Up @@ -32,6 +33,21 @@ impl forall [N:! IntLiteral()] UInt(N) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

// TODO: Allow as an implicit conversion if N > M.
impl forall [M:! IntLiteral(), N:! IntLiteral()] UInt(M) as As(UInt(N)) {
fn Convert[self: Self]() -> UInt(N) = "int.convert";
}

// TODO: Allow as an implicit conversion if N > M.
impl forall [M:! IntLiteral(), N:! IntLiteral()] UInt(M) as As(Int(N)) {
fn Convert[self: Self]() -> Int(N) = "int.convert";
}

// Never implicit.
impl forall [M:! IntLiteral(), N:! IntLiteral()] Int(M) as As(UInt(N)) {
fn Convert[self: Self]() -> UInt(N) = "int.convert";
}

// Comparisons.

impl forall [N:! IntLiteral()] UInt(N) as Eq {
Expand Down
26 changes: 26 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,26 @@ static auto ValidateFloatType(Context& context, SemIRLoc loc,
return ValidateFloatBitWidth(context, loc, result.bit_width_id);
}

// Performs a conversion between integer types, truncating if the value doesn't
// fit in the destination type.
static auto PerformIntConvert(Context& context, SemIR::InstId arg_id,
SemIR::TypeId dest_type_id) -> SemIR::ConstantId {
auto arg_val =
context.ints().Get(context.insts().GetAs<SemIR::IntValue>(arg_id).int_id);
auto [dest_is_signed, bit_width_id] =
context.sem_ir().types().GetIntTypeInfo(dest_type_id);
if (bit_width_id.is_valid()) {
// TODO: If the value fits in the destination type, reuse the existing
// int_id rather than recomputing it. This is probably the most common case.
bool src_is_signed = context.sem_ir().types().IsSignedInt(
context.insts().Get(arg_id).type_id());
unsigned width = context.ints().Get(bit_width_id).getZExtValue();
arg_val =
src_is_signed ? arg_val.sextOrTrunc(width) : arg_val.zextOrTrunc(width);
}
return MakeIntResult(context, dest_type_id, dest_is_signed, arg_val);
}

// Performs a conversion between integer types, diagnosing if the value doesn't
// fit in the destination type.
static auto PerformCheckedIntConvert(Context& context, SemIRLoc loc,
Expand Down Expand Up @@ -1284,6 +1304,12 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc,
}

// Integer conversions.
case SemIR::BuiltinFunctionKind::IntConvert: {
if (phase == Phase::Symbolic) {
return MakeConstantResult(context, call, phase);
}
return PerformIntConvert(context, arg_ids[0], call.type_id);
}
case SemIR::BuiltinFunctionKind::IntConvertChecked: {
if (phase == Phase::Symbolic) {
return MakeConstantResult(context, call, phase);
Expand Down
226 changes: 226 additions & 0 deletions toolchain/check/testdata/builtins/int/convert.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// 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
//
// EXTRA-ARGS: --no-dump-sem-ir
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/convert.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/convert.carbon

// --- int_ops.carbon

library "[[@TEST_NAME]]";

// Size preserving
fn Int32ToInt32(a: i32) -> i32 = "int.convert";
fn Int32ToUint32(a: i32) -> u32 = "int.convert";
fn Uint32ToInt32(a: u32) -> i32 = "int.convert";
fn Uint32ToUint32(a: u32) -> u32 = "int.convert";
fn IntLiteralToIntLiteral(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.convert";

// Narrowing
fn Int32ToInt16(a: i32) -> i16 = "int.convert";
fn Int32ToUint16(a: i32) -> u16 = "int.convert";
fn Uint32ToInt16(a: u32) -> i16 = "int.convert";
fn Uint32ToUint16(a: u32) -> u16 = "int.convert";
fn IntLiteralToInt16(a: Core.IntLiteral()) -> i16 = "int.convert";
fn IntLiteralToUint16(a: Core.IntLiteral()) -> u16 = "int.convert";

// Widening
fn Int32ToInt64(a: i32) -> i64 = "int.convert";
fn Int32ToUint64(a: i32) -> u64 = "int.convert";
fn Uint32ToInt64(a: u32) -> i64 = "int.convert";
fn Uint32ToUint64(a: u32) -> u64 = "int.convert";
fn Int32ToIntLiteral(a: i32) -> Core.IntLiteral() = "int.convert";
fn Uint32ToIntLiteral(a: u32) -> Core.IntLiteral() = "int.convert";

class Expect[T:! type](N:! T) {}
fn Test[T:! type](N:! T) -> Expect(N) { return {}; }

// --- fail_self_test.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
// Ensure our testing machinery works.
// CHECK:STDERR: fail_self_test.carbon:[[@LINE+7]]:3: error: cannot convert from `Expect(0)` to `Expect(1)` with `as` [ExplicitAsConversionFailure]
// CHECK:STDERR: Test(Int32ToInt32(0)) as Expect(1 as i32);
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_self_test.carbon:[[@LINE+4]]:3: note: type `Expect(0)` does not implement interface `Core.As(Expect(1))` [MissingImplInMemberAccessNote]
// CHECK:STDERR: Test(Int32ToInt32(0)) as Expect(1 as i32);
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
Test(Int32ToInt32(0)) as Expect(1 as i32);
}

// --- identity.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
Test(Int32ToInt32(-0x8000_0000)) as Expect(-0x8000_0000 as i32);
Test(Int32ToInt32(-1)) as Expect(-1 as i32);
Test(Int32ToInt32(0)) as Expect(0 as i32);
Test(Int32ToInt32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i32);

Test(Uint32ToUint32(0)) as Expect(0 as u32);
Test(Uint32ToUint32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u32);
Test(Uint32ToUint32(0x8000_0000)) as Expect(0x8000_0000 as u32);
Test(Uint32ToUint32(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as u32);

Test(IntLiteralToIntLiteral(0x1_0000_0000_0000_0000)) as
Expect(0x1_0000_0000_0000_0000);
Test(IntLiteralToIntLiteral(-1)) as Expect(-1);
}

// --- same_size.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
Test(Int32ToUint32(-0x8000_0000)) as Expect(0x8000_0000 as u32);
Test(Int32ToUint32(-1)) as Expect(0xFFFF_FFFF as u32);
Test(Int32ToUint32(0)) as Expect(0 as u32);
Test(Int32ToUint32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u32);

Test(Uint32ToInt32(0)) as Expect(0 as i32);
Test(Uint32ToInt32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i32);
Test(Uint32ToInt32(0x8000_0000)) as Expect(-0x8000_0000 as i32);
Test(Uint32ToInt32(0xFFFF_FFFF)) as Expect(-1 as i32);
}

// --- truncate.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
Test(Int32ToInt16(-0x8000_0000)) as Expect(0 as i16);
Test(Int32ToInt16(-0x7FFF_EDCC)) as Expect(0x1234 as i16);
Test(Int32ToInt16(-0x7FFF_1234)) as Expect(-0x1234 as i16);
Test(Int32ToInt16(-0x8000)) as Expect(-0x8000 as i16);
Test(Int32ToInt16(-1)) as Expect(-1 as i16);
Test(Int32ToInt16(0)) as Expect(0 as i16);
Test(Int32ToInt16(0x7FFF)) as Expect(0x7FFF as i16);
Test(Int32ToInt16(0xFFFF)) as Expect(-1 as i16);
Test(Int32ToInt16(0x7FFF_1234)) as Expect(0x1234 as i16);
Test(Int32ToInt16(0x7FFF_EDCC)) as Expect(-0x1234 as i16);
Test(Int32ToInt16(0x7FFF_FFFF)) as Expect(-1 as i16);

Test(Int32ToUint16(-0x8000_0000)) as Expect(0 as u16);
Test(Int32ToUint16(-0x7FFF_EDCC)) as Expect(0x1234 as u16);
Test(Int32ToUint16(-0x7FFF_1234)) as Expect(0xEDCC as u16);
Test(Int32ToUint16(-0x8000)) as Expect(0x8000 as u16);
Test(Int32ToUint16(-1)) as Expect(0xFFFF as u16);
Test(Int32ToUint16(0)) as Expect(0 as u16);
Test(Int32ToUint16(0x7FFF)) as Expect(0x7FFF as u16);
Test(Int32ToUint16(0xFFFF)) as Expect(0xFFFF as u16);
Test(Int32ToUint16(0x7FFF_1234)) as Expect(0x1234 as u16);
Test(Int32ToUint16(0x7FFF_EDCC)) as Expect(0xEDCC as u16);
Test(Int32ToUint16(0x7FFF_FFFF)) as Expect(0xFFFF as u16);

Test(Uint32ToInt16(0x8000_0000)) as Expect(0 as i16);
Test(Uint32ToInt16(0xFFFF_1234)) as Expect(0x1234 as i16);
Test(Uint32ToInt16(0xFFFF_EDCC)) as Expect(-0x1234 as i16);
Test(Uint32ToInt16(0xFFFF_8000)) as Expect(-0x8000 as i16);
Test(Uint32ToInt16(0xFFFF_FFFF)) as Expect(-1 as i16);
Test(Uint32ToInt16(0)) as Expect(0 as i16);
Test(Uint32ToInt16(0x7FFF)) as Expect(0x7FFF as i16);
Test(Uint32ToInt16(0xFFFF)) as Expect(-1 as i16);
Test(Uint32ToInt16(0x7FFF_1234)) as Expect(0x1234 as i16);
Test(Uint32ToInt16(0x7FFF_EDCC)) as Expect(-0x1234 as i16);
Test(Uint32ToInt16(0x7FFF_FFFF)) as Expect(-1 as i16);

Test(Uint32ToUint16(0x8000_0000)) as Expect(0 as u16);
Test(Uint32ToUint16(0xFFFF_1234)) as Expect(0x1234 as u16);
Test(Uint32ToUint16(0xFFFF_EDCC)) as Expect(0xEDCC as u16);
Test(Uint32ToUint16(0xFFFF_8000)) as Expect(0x8000 as u16);
Test(Uint32ToUint16(0xFFFF_FFFF)) as Expect(0xFFFF as u16);
Test(Uint32ToUint16(0)) as Expect(0 as u16);
Test(Uint32ToUint16(0x7FFF)) as Expect(0x7FFF as u16);
Test(Uint32ToUint16(0xFFFF)) as Expect(0xFFFF as u16);
Test(Uint32ToUint16(0x7FFF_1234)) as Expect(0x1234 as u16);
Test(Uint32ToUint16(0x7FFF_EDCC)) as Expect(0xEDCC as u16);
Test(Uint32ToUint16(0x7FFF_FFFF)) as Expect(0xFFFF as u16);

Test(IntLiteralToInt16(0)) as Expect(0 as i16);
Test(IntLiteralToInt16(0x7FFF)) as Expect(0x7FFF as i16);
Test(IntLiteralToInt16(0x8000)) as Expect(-0x8000 as i16);
Test(IntLiteralToInt16(0xFFFF)) as Expect(-1 as i16);
Test(IntLiteralToInt16(0x1_2345)) as Expect(0x2345 as i16);
Test(IntLiteralToInt16(-1)) as Expect(-1 as i16);

Test(IntLiteralToUint16(0)) as Expect(0 as u16);
Test(IntLiteralToUint16(0x7FFF)) as Expect(0x7FFF as u16);
Test(IntLiteralToUint16(0x8000)) as Expect(0x8000 as u16);
Test(IntLiteralToUint16(0xFFFF)) as Expect(0xFFFF as u16);
Test(IntLiteralToUint16(0x1_2345)) as Expect(0x2345 as u16);
Test(IntLiteralToUint16(-1)) as Expect(0xFFFF as u16);
}

// --- zero_extend.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
Test(Uint32ToInt64(0)) as Expect(0 as i64);
Test(Uint32ToInt64(0x1234_5678)) as Expect(0x1234_5678 as i64);
Test(Uint32ToInt64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i64);
Test(Uint32ToInt64(0x8000_0000)) as Expect(0x8000_0000 as i64);
Test(Uint32ToInt64(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as i64);

Test(Uint32ToUint64(0)) as Expect(0 as u64);
Test(Uint32ToUint64(0x1234_5678)) as Expect(0x1234_5678 as u64);
Test(Uint32ToUint64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u64);
Test(Uint32ToUint64(0x8000_0000)) as Expect(0x8000_0000 as u64);
Test(Uint32ToUint64(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as u64);

Test(Uint32ToIntLiteral(0x1234_5678)) as Expect(0x1234_5678);
Test(Uint32ToIntLiteral(0x8765_4321)) as Expect(0x8765_4321);
Test(Uint32ToIntLiteral(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF);
}

// --- sign_extend.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

fn F() {
Test(Int32ToInt64(0)) as Expect(0 as i64);
Test(Int32ToInt64(0x1234_5678)) as Expect(0x1234_5678 as i64);
Test(Int32ToInt64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i64);
Test(Int32ToInt64(-1)) as Expect(-1 as i64);

Test(Int32ToUint64(0)) as Expect(0 as u64);
Test(Int32ToUint64(0x1234_5678)) as Expect(0x1234_5678 as u64);
Test(Int32ToUint64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u64);
Test(Int32ToUint64(-1)) as Expect(0xFFFF_FFFF_FFFF_FFFF as u64);
Test(Int32ToUint64(-0x8000_0000)) as Expect(0xFFFF_FFFF_8000_0000 as u64);

Test(Int32ToIntLiteral(0x1234_5678)) as Expect(0x1234_5678);
Test(Int32ToIntLiteral(-0x1234_5678)) as Expect(-0x1234_5678);
Test(Int32ToIntLiteral(-1)) as Expect(-1);
}

// --- fail_not_constant.carbon

library "[[@TEST_NAME]]";
import library "int_ops";

let not_constant: Core.IntLiteral() = 0;

// CHECK:STDERR: fail_not_constant.carbon:[[@LINE+7]]:33: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction]
// CHECK:STDERR: let convert_not_constant: i16 = IntLiteralToInt16(not_constant);
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_not_constant.carbon:[[@LINE-7]]:1: in import [InImport]
// CHECK:STDERR: int_ops.carbon:16:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere]
// CHECK:STDERR: fn IntLiteralToInt16(a: Core.IntLiteral()) -> i16 = "int.convert";
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let convert_not_constant: i16 = IntLiteralToInt16(not_constant);
2 changes: 1 addition & 1 deletion toolchain/check/testdata/class/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -319,5 +319,5 @@ fn Run() {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F[%self.param_patt: %ForwardDeclared.7b34f2.1]() [from "a.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G[addr <unexpected>.inst1019: %ptr.6cf]() [from "a.carbon"];
// CHECK:STDOUT: fn @G[addr <unexpected>.inst1063: %ptr.6cf]() [from "a.carbon"];
// CHECK:STDOUT:
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ fn G(n: i32) {
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template]
// CHECK:STDOUT: %G.type: type = fn_type @G [template]
// CHECK:STDOUT: %G: %G.type = struct_value () [template]
// CHECK:STDOUT: %impl_witness.a03: <witness> = impl_witness (imports.%import_ref.c16), @impl.13(%int_32) [template]
// CHECK:STDOUT: %Op.type.16e: type = fn_type @Op.2, @impl.13(%int_32) [template]
// CHECK:STDOUT: %impl_witness.a03: <witness> = impl_witness (imports.%import_ref.c16), @impl.14(%int_32) [template]
// CHECK:STDOUT: %Op.type.16e: type = fn_type @Op.2, @impl.14(%int_32) [template]
// CHECK:STDOUT: %Op.ceb: %Op.type.16e = struct_value () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
Expand Down
6 changes: 3 additions & 3 deletions toolchain/check/testdata/struct/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1017 [no loc], unloaded
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1061 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down Expand Up @@ -396,7 +396,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1017 [no loc], unloaded
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1061 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down Expand Up @@ -486,7 +486,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1017 [no loc], unloaded
// CHECK:STDOUT: %import_ref.5c7 = import_ref Implicit//default, inst1061 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down
6 changes: 3 additions & 3 deletions toolchain/check/testdata/tuple/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ var c_bad: C((3, 4)) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1052 [no loc], unloaded
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1096 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down Expand Up @@ -421,7 +421,7 @@ var c_bad: C((3, 4)) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1052 [no loc], unloaded
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1096 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down Expand Up @@ -511,7 +511,7 @@ var c_bad: C((3, 4)) = F();
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.357]
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1052 [no loc], unloaded
// CHECK:STDOUT: %import_ref.2e0 = import_ref Implicit//default, inst1096 [no loc], unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
Expand Down
Loading

0 comments on commit 246ec78

Please sign in to comment.