Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for converting between integer types #4753

Merged
merged 7 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
}
Comment on lines +35 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all of these, and maybe better in a follow-up, would it make more sense to write these as "from N bits to M bits", and make that consistent? I find it a bit surprising which is N and which is M here.

Or maybe the bit widths should be From and To? Or FromBitwidth and ToBitwidth?


// 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.
chandlerc marked this conversation as resolved.
Show resolved Hide resolved
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
Loading