From c1590f886a07a9374345dc6e00f17f14851452f9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 17 Dec 2024 16:47:03 -0800 Subject: [PATCH] Add equality comparison support for `bool`. (#4701) --- core/prelude/operators/comparison.carbon | 8 + toolchain/check/eval.cpp | 22 + .../check/testdata/builtins/bool/eq.carbon | 428 ++++++++++++++++++ .../check/testdata/builtins/bool/neq.carbon | 428 ++++++++++++++++++ .../testdata/operators/overloaded/eq.carbon | 10 +- .../operators/overloaded/ordered.carbon | 4 +- toolchain/lower/handle_call.cpp | 6 +- .../testdata/builtins/no_prelude/bool.carbon | 70 +++ toolchain/sem_ir/builtin_function_kind.cpp | 8 + toolchain/sem_ir/builtin_function_kind.def | 4 + 10 files changed, 980 insertions(+), 8 deletions(-) create mode 100644 toolchain/check/testdata/builtins/bool/eq.carbon create mode 100644 toolchain/check/testdata/builtins/bool/neq.carbon create mode 100644 toolchain/lower/testdata/builtins/no_prelude/bool.carbon diff --git a/core/prelude/operators/comparison.carbon b/core/prelude/operators/comparison.carbon index 03e2ea3069d24..7a76a70361f83 100644 --- a/core/prelude/operators/comparison.carbon +++ b/core/prelude/operators/comparison.carbon @@ -20,3 +20,11 @@ interface Ordered { fn Greater[self: Self](other: Self) -> bool; fn GreaterOrEquivalent[self: Self](other: Self) -> bool; } + +// Equality comparison for `bool`. +// Note that this must be provided in this library as `bool` doesn't have any +// associated libraries of its own. +impl bool as Eq { + fn Equal[self: Self](other: Self) -> bool = "bool.eq"; + fn NotEqual[self: Self](other: Self) -> bool = "bool.neq"; +} diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 27dae45cc681d..87c680b54e324 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -1097,6 +1097,18 @@ static auto PerformBuiltinFloatComparison( return MakeBoolResult(context, bool_type_id, result); } +// Performs a builtin boolean comparison. +static auto PerformBuiltinBoolComparison( + Context& context, SemIR::BuiltinFunctionKind builtin_kind, + SemIR::InstId lhs_id, SemIR::InstId rhs_id, SemIR::TypeId bool_type_id) { + bool lhs = context.insts().GetAs(lhs_id).value.ToBool(); + bool rhs = context.insts().GetAs(rhs_id).value.ToBool(); + return MakeBoolResult(context, bool_type_id, + builtin_kind == SemIR::BuiltinFunctionKind::BoolEq + ? lhs == rhs + : lhs != rhs); +} + // Returns a constant for a call to a builtin function. static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, SemIR::Call call, @@ -1235,6 +1247,16 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, return PerformBuiltinFloatComparison(context, builtin_kind, arg_ids[0], arg_ids[1], call.type_id); } + + // Bool comparisons. + case SemIR::BuiltinFunctionKind::BoolEq: + case SemIR::BuiltinFunctionKind::BoolNeq: { + if (phase != Phase::Template) { + break; + } + return PerformBuiltinBoolComparison(context, builtin_kind, arg_ids[0], + arg_ids[1], call.type_id); + } } return SemIR::ConstantId::NotConstant; diff --git a/toolchain/check/testdata/builtins/bool/eq.carbon b/toolchain/check/testdata/builtins/bool/eq.carbon new file mode 100644 index 0000000000000..082e5ecf25335 --- /dev/null +++ b/toolchain/check/testdata/builtins/bool/eq.carbon @@ -0,0 +1,428 @@ +// 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 +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/bool/eq.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/bool/eq.carbon + +// --- builtin_call.carbon + +library "[[@TEST_NAME]]"; + +fn Eq(a: bool, b: bool) -> bool = "bool.eq"; + +class C(B:! bool) {} + +fn True() -> C(true); +fn False() -> C(false); + +var a: C(Eq(true, true)) = True(); +var b: C(Eq(true, false)) = False(); +var c: C(Eq(false, true)) = False(); +var d: C(Eq(false, false)) = True(); + +// --- prelude.carbon + +library "[[@TEST_NAME]]"; + +class C(B:! bool) {} + +fn True() -> C(true); +fn False() -> C(false); + +var a: C(true == true) = True(); +var b: C(true == false) = False(); +var c: C(false == true) = False(); +var d: C(false == false) = True(); + +// CHECK:STDOUT: --- builtin_call.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [template] +// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [template] +// CHECK:STDOUT: %Eq.type: type = fn_type @Eq [template] +// CHECK:STDOUT: %Eq: %Eq.type = struct_value () [template] +// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic] +// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.1: type = class_type @C, @C(%B) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %true: bool = bool_literal true [template] +// CHECK:STDOUT: %C.2: type = class_type @C, @C(%true) [template] +// CHECK:STDOUT: %True.type: type = fn_type @True [template] +// CHECK:STDOUT: %True: %True.type = struct_value () [template] +// CHECK:STDOUT: %false: bool = bool_literal false [template] +// CHECK:STDOUT: %C.3: type = class_type @C, @C(%false) [template] +// CHECK:STDOUT: %False.type: type = fn_type @False [template] +// CHECK:STDOUT: %False: %False.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Bool = %import_ref +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Eq = %Eq.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .True = %True.decl +// CHECK:STDOUT: .False = %False.decl +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Eq.decl: %Eq.type = fn_decl @Eq [template = constants.%Eq] { +// CHECK:STDOUT: %a.patt: bool = binding_pattern a +// CHECK:STDOUT: %a.param_patt: bool = value_param_pattern %a.patt, runtime_param0 +// CHECK:STDOUT: %b.patt: bool = binding_pattern b +// CHECK:STDOUT: %b.param_patt: bool = value_param_pattern %b.patt, runtime_param1 +// CHECK:STDOUT: %return.patt: bool = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: bool = out_param_pattern %return.patt, runtime_param2 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type.loc4_10: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_10.1: type = value_of_initializer %bool.make_type.loc4_10 [template = bool] +// CHECK:STDOUT: %.loc4_10.2: type = converted %bool.make_type.loc4_10, %.loc4_10.1 [template = bool] +// CHECK:STDOUT: %bool.make_type.loc4_19: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_19.1: type = value_of_initializer %bool.make_type.loc4_19 [template = bool] +// CHECK:STDOUT: %.loc4_19.2: type = converted %bool.make_type.loc4_19, %.loc4_19.1 [template = bool] +// CHECK:STDOUT: %bool.make_type.loc4_28: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_28.1: type = value_of_initializer %bool.make_type.loc4_28 [template = bool] +// CHECK:STDOUT: %.loc4_28.2: type = converted %bool.make_type.loc4_28, %.loc4_28.1 [template = bool] +// CHECK:STDOUT: %a.param: bool = value_param runtime_param0 +// CHECK:STDOUT: %a: bool = bind_name a, %a.param +// CHECK:STDOUT: %b.param: bool = value_param runtime_param1 +// CHECK:STDOUT: %b: bool = bind_name b, %b.param +// CHECK:STDOUT: %return.param: ref bool = out_param runtime_param2 +// CHECK:STDOUT: %return: ref bool = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %B.patt.loc6_9.1: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: %B.param_patt: bool = value_param_pattern %B.patt.loc6_9.1, runtime_param [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc6_13.1: type = value_of_initializer %bool.make_type [template = bool] +// CHECK:STDOUT: %.loc6_13.2: type = converted %bool.make_type, %.loc6_13.1 [template = bool] +// CHECK:STDOUT: %B.param: bool = value_param runtime_param +// CHECK:STDOUT: %B.loc6_9.1: bool = bind_symbolic_name B, 0, %B.param [symbolic = %B.loc6_9.2 (constants.%B)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %True.decl: %True.type = fn_decl @True [template = constants.%True] { +// CHECK:STDOUT: %return.patt: %C.2 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.2 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %return.param: ref %C.2 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.2 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %False.decl: %False.type = fn_decl @False [template = constants.%False] { +// CHECK:STDOUT: %return.patt: %C.3 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.3 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %return.param: ref %C.3 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.3 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref.loc11: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Eq.ref.loc11: %Eq.type = name_ref Eq, %Eq.decl [template = constants.%Eq] +// CHECK:STDOUT: %true.loc11_13: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %true.loc11_19: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %bool.eq.loc11: init bool = call %Eq.ref.loc11(%true.loc11_13, %true.loc11_19) [template = constants.%true] +// CHECK:STDOUT: %.loc11_24.1: bool = value_of_initializer %bool.eq.loc11 [template = constants.%true] +// CHECK:STDOUT: %.loc11_24.2: bool = converted %bool.eq.loc11, %.loc11_24.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc11: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %a.var: ref %C.2 = var a +// CHECK:STDOUT: %a: ref %C.2 = bind_name a, %a.var +// CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Eq.ref.loc12: %Eq.type = name_ref Eq, %Eq.decl [template = constants.%Eq] +// CHECK:STDOUT: %true.loc12: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %false.loc12: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %bool.eq.loc12: init bool = call %Eq.ref.loc12(%true.loc12, %false.loc12) [template = constants.%false] +// CHECK:STDOUT: %.loc12_25.1: bool = value_of_initializer %bool.eq.loc12 [template = constants.%false] +// CHECK:STDOUT: %.loc12_25.2: bool = converted %bool.eq.loc12, %.loc12_25.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %b.var: ref %C.3 = var b +// CHECK:STDOUT: %b: ref %C.3 = bind_name b, %b.var +// CHECK:STDOUT: %C.ref.loc13: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Eq.ref.loc13: %Eq.type = name_ref Eq, %Eq.decl [template = constants.%Eq] +// CHECK:STDOUT: %false.loc13: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %true.loc13: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %bool.eq.loc13: init bool = call %Eq.ref.loc13(%false.loc13, %true.loc13) [template = constants.%false] +// CHECK:STDOUT: %.loc13_25.1: bool = value_of_initializer %bool.eq.loc13 [template = constants.%false] +// CHECK:STDOUT: %.loc13_25.2: bool = converted %bool.eq.loc13, %.loc13_25.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc13: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %c.var: ref %C.3 = var c +// CHECK:STDOUT: %c: ref %C.3 = bind_name c, %c.var +// CHECK:STDOUT: %C.ref.loc14: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Eq.ref.loc14: %Eq.type = name_ref Eq, %Eq.decl [template = constants.%Eq] +// CHECK:STDOUT: %false.loc14_13: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %false.loc14_20: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %bool.eq.loc14: init bool = call %Eq.ref.loc14(%false.loc14_13, %false.loc14_20) [template = constants.%true] +// CHECK:STDOUT: %.loc14_26.1: bool = value_of_initializer %bool.eq.loc14 [template = constants.%true] +// CHECK:STDOUT: %.loc14_26.2: bool = converted %bool.eq.loc14, %.loc14_26.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc14: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %d.var: ref %C.2 = var d +// CHECK:STDOUT: %d: ref %C.2 = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%B.loc6_9.1: bool) { +// CHECK:STDOUT: %B.loc6_9.2: bool = bind_symbolic_name B, 0 [symbolic = %B.loc6_9.2 (constants.%B)] +// CHECK:STDOUT: %B.patt.loc6_9.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Eq(%a.param_patt: bool, %b.param_patt: bool) -> bool = "bool.eq"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @True() -> %C.2; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @False() -> %C.3; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %True.ref.loc11: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc11: ref %C.2 = splice_block file.%a.var {} +// CHECK:STDOUT: %True.call.loc11: init %C.2 = call %True.ref.loc11() to %.loc11 +// CHECK:STDOUT: assign file.%a.var, %True.call.loc11 +// CHECK:STDOUT: %False.ref.loc12: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc12: ref %C.3 = splice_block file.%b.var {} +// CHECK:STDOUT: %False.call.loc12: init %C.3 = call %False.ref.loc12() to %.loc12 +// CHECK:STDOUT: assign file.%b.var, %False.call.loc12 +// CHECK:STDOUT: %False.ref.loc13: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc13: ref %C.3 = splice_block file.%c.var {} +// CHECK:STDOUT: %False.call.loc13: init %C.3 = call %False.ref.loc13() to %.loc13 +// CHECK:STDOUT: assign file.%c.var, %False.call.loc13 +// CHECK:STDOUT: %True.ref.loc14: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc14: ref %C.2 = splice_block file.%d.var {} +// CHECK:STDOUT: %True.call.loc14: init %C.2 = call %True.ref.loc14() to %.loc14 +// CHECK:STDOUT: assign file.%d.var, %True.call.loc14 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%B) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%B +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%B +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%true) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%true +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%true +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%false) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%false +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%false +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- prelude.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [template] +// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [template] +// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic] +// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.1: type = class_type @C, @C(%B) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %true: bool = bool_literal true [template] +// CHECK:STDOUT: %C.2: type = class_type @C, @C(%true) [template] +// CHECK:STDOUT: %True.type: type = fn_type @True [template] +// CHECK:STDOUT: %True: %True.type = struct_value () [template] +// CHECK:STDOUT: %false: bool = bool_literal false [template] +// CHECK:STDOUT: %C.3: type = class_type @C, @C(%false) [template] +// CHECK:STDOUT: %False.type: type = fn_type @False [template] +// CHECK:STDOUT: %False: %False.type = struct_value () [template] +// CHECK:STDOUT: %Equal.type.1: type = fn_type @Equal.1 [template] +// CHECK:STDOUT: %NotEqual.type: type = fn_type @NotEqual [template] +// CHECK:STDOUT: %NotEqual: %NotEqual.type = struct_value () [template] +// CHECK:STDOUT: %Equal.type.2: type = fn_type @Equal.2 [template] +// CHECK:STDOUT: %Equal.2: %Equal.type.2 = struct_value () [template] +// CHECK:STDOUT: %interface: = interface_witness (%Equal.2, %NotEqual) [template] +// CHECK:STDOUT: %Equal.bound.1: = bound_method %true, %Equal.2 [template] +// CHECK:STDOUT: %Equal.bound.2: = bound_method %false, %Equal.2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Bool = %import_ref.1 +// CHECK:STDOUT: .Eq = %import_ref.2 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .True = %True.decl +// CHECK:STDOUT: .False = %False.decl +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %B.patt.loc4_9.1: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: %B.param_patt: bool = value_param_pattern %B.patt.loc4_9.1, runtime_param [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_13.1: type = value_of_initializer %bool.make_type [template = bool] +// CHECK:STDOUT: %.loc4_13.2: type = converted %bool.make_type, %.loc4_13.1 [template = bool] +// CHECK:STDOUT: %B.param: bool = value_param runtime_param +// CHECK:STDOUT: %B.loc4_9.1: bool = bind_symbolic_name B, 0, %B.param [symbolic = %B.loc4_9.2 (constants.%B)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %True.decl: %True.type = fn_decl @True [template = constants.%True] { +// CHECK:STDOUT: %return.patt: %C.2 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.2 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %return.param: ref %C.2 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.2 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %False.decl: %False.type = fn_decl @False [template = constants.%False] { +// CHECK:STDOUT: %return.patt: %C.3 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.3 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %return.param: ref %C.3 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.3 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref.loc9: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true.loc9_10: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %true.loc9_18: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %impl.elem0.loc9: %Equal.type.1 = interface_witness_access constants.%interface, element0 [template = constants.%Equal.2] +// CHECK:STDOUT: %Equal.bound.loc9: = bound_method %true.loc9_10, %impl.elem0.loc9 [template = constants.%Equal.bound.1] +// CHECK:STDOUT: %bool.eq.loc9: init bool = call %Equal.bound.loc9(%true.loc9_10, %true.loc9_18) [template = constants.%true] +// CHECK:STDOUT: %.loc9_22.1: bool = value_of_initializer %bool.eq.loc9 [template = constants.%true] +// CHECK:STDOUT: %.loc9_22.2: bool = converted %bool.eq.loc9, %.loc9_22.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc9: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %a.var: ref %C.2 = var a +// CHECK:STDOUT: %a: ref %C.2 = bind_name a, %a.var +// CHECK:STDOUT: %C.ref.loc10: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true.loc10: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %false.loc10: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %impl.elem0.loc10: %Equal.type.1 = interface_witness_access constants.%interface, element0 [template = constants.%Equal.2] +// CHECK:STDOUT: %Equal.bound.loc10: = bound_method %true.loc10, %impl.elem0.loc10 [template = constants.%Equal.bound.1] +// CHECK:STDOUT: %bool.eq.loc10: init bool = call %Equal.bound.loc10(%true.loc10, %false.loc10) [template = constants.%false] +// CHECK:STDOUT: %.loc10_23.1: bool = value_of_initializer %bool.eq.loc10 [template = constants.%false] +// CHECK:STDOUT: %.loc10_23.2: bool = converted %bool.eq.loc10, %.loc10_23.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc10: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %b.var: ref %C.3 = var b +// CHECK:STDOUT: %b: ref %C.3 = bind_name b, %b.var +// CHECK:STDOUT: %C.ref.loc11: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false.loc11: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %true.loc11: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %impl.elem0.loc11: %Equal.type.1 = interface_witness_access constants.%interface, element0 [template = constants.%Equal.2] +// CHECK:STDOUT: %Equal.bound.loc11: = bound_method %false.loc11, %impl.elem0.loc11 [template = constants.%Equal.bound.2] +// CHECK:STDOUT: %bool.eq.loc11: init bool = call %Equal.bound.loc11(%false.loc11, %true.loc11) [template = constants.%false] +// CHECK:STDOUT: %.loc11_23.1: bool = value_of_initializer %bool.eq.loc11 [template = constants.%false] +// CHECK:STDOUT: %.loc11_23.2: bool = converted %bool.eq.loc11, %.loc11_23.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc11: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %c.var: ref %C.3 = var c +// CHECK:STDOUT: %c: ref %C.3 = bind_name c, %c.var +// CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false.loc12_10: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %false.loc12_19: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %impl.elem0.loc12: %Equal.type.1 = interface_witness_access constants.%interface, element0 [template = constants.%Equal.2] +// CHECK:STDOUT: %Equal.bound.loc12: = bound_method %false.loc12_10, %impl.elem0.loc12 [template = constants.%Equal.bound.2] +// CHECK:STDOUT: %bool.eq.loc12: init bool = call %Equal.bound.loc12(%false.loc12_10, %false.loc12_19) [template = constants.%true] +// CHECK:STDOUT: %.loc12_24.1: bool = value_of_initializer %bool.eq.loc12 [template = constants.%true] +// CHECK:STDOUT: %.loc12_24.2: bool = converted %bool.eq.loc12, %.loc12_24.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %d.var: ref %C.2 = var d +// CHECK:STDOUT: %d: ref %C.2 = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%B.loc4_9.1: bool) { +// CHECK:STDOUT: %B.loc4_9.2: bool = bind_symbolic_name B, 0 [symbolic = %B.loc4_9.2 (constants.%B)] +// CHECK:STDOUT: %B.patt.loc4_9.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @True() -> %C.2; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @False() -> %C.3; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %True.ref.loc9: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc9: ref %C.2 = splice_block file.%a.var {} +// CHECK:STDOUT: %True.call.loc9: init %C.2 = call %True.ref.loc9() to %.loc9 +// CHECK:STDOUT: assign file.%a.var, %True.call.loc9 +// CHECK:STDOUT: %False.ref.loc10: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc10: ref %C.3 = splice_block file.%b.var {} +// CHECK:STDOUT: %False.call.loc10: init %C.3 = call %False.ref.loc10() to %.loc10 +// CHECK:STDOUT: assign file.%b.var, %False.call.loc10 +// CHECK:STDOUT: %False.ref.loc11: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc11: ref %C.3 = splice_block file.%c.var {} +// CHECK:STDOUT: %False.call.loc11: init %C.3 = call %False.ref.loc11() to %.loc11 +// CHECK:STDOUT: assign file.%c.var, %False.call.loc11 +// CHECK:STDOUT: %True.ref.loc12: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc12: ref %C.2 = splice_block file.%d.var {} +// CHECK:STDOUT: %True.call.loc12: init %C.2 = call %True.ref.loc12() to %.loc12 +// CHECK:STDOUT: assign file.%d.var, %True.call.loc12 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%B) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%B +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%B +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%true) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%true +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%true +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%false) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%false +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%false +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/bool/neq.carbon b/toolchain/check/testdata/builtins/bool/neq.carbon new file mode 100644 index 0000000000000..bfa5a1df54902 --- /dev/null +++ b/toolchain/check/testdata/builtins/bool/neq.carbon @@ -0,0 +1,428 @@ +// 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 +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/bool/neq.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/bool/neq.carbon + +// --- builtin_call.carbon + +library "[[@TEST_NAME]]"; + +fn Neq(a: bool, b: bool) -> bool = "bool.neq"; + +class C(B:! bool) {} + +fn True() -> C(true); +fn False() -> C(false); + +var a: C(Neq(true, true)) = False(); +var b: C(Neq(true, false)) = True(); +var c: C(Neq(false, true)) = True(); +var d: C(Neq(false, false)) = False(); + +// --- prelude.carbon + +library "[[@TEST_NAME]]"; + +class C(B:! bool) {} + +fn True() -> C(true); +fn False() -> C(false); + +var a: C(true != true) = False(); +var b: C(true != false) = True(); +var c: C(false != true) = True(); +var d: C(false != false) = False(); + +// CHECK:STDOUT: --- builtin_call.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [template] +// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [template] +// CHECK:STDOUT: %Neq.type: type = fn_type @Neq [template] +// CHECK:STDOUT: %Neq: %Neq.type = struct_value () [template] +// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic] +// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.1: type = class_type @C, @C(%B) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %true: bool = bool_literal true [template] +// CHECK:STDOUT: %C.2: type = class_type @C, @C(%true) [template] +// CHECK:STDOUT: %True.type: type = fn_type @True [template] +// CHECK:STDOUT: %True: %True.type = struct_value () [template] +// CHECK:STDOUT: %false: bool = bool_literal false [template] +// CHECK:STDOUT: %C.3: type = class_type @C, @C(%false) [template] +// CHECK:STDOUT: %False.type: type = fn_type @False [template] +// CHECK:STDOUT: %False: %False.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Bool = %import_ref +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Neq = %Neq.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .True = %True.decl +// CHECK:STDOUT: .False = %False.decl +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Neq.decl: %Neq.type = fn_decl @Neq [template = constants.%Neq] { +// CHECK:STDOUT: %a.patt: bool = binding_pattern a +// CHECK:STDOUT: %a.param_patt: bool = value_param_pattern %a.patt, runtime_param0 +// CHECK:STDOUT: %b.patt: bool = binding_pattern b +// CHECK:STDOUT: %b.param_patt: bool = value_param_pattern %b.patt, runtime_param1 +// CHECK:STDOUT: %return.patt: bool = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: bool = out_param_pattern %return.patt, runtime_param2 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type.loc4_11: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_11.1: type = value_of_initializer %bool.make_type.loc4_11 [template = bool] +// CHECK:STDOUT: %.loc4_11.2: type = converted %bool.make_type.loc4_11, %.loc4_11.1 [template = bool] +// CHECK:STDOUT: %bool.make_type.loc4_20: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_20.1: type = value_of_initializer %bool.make_type.loc4_20 [template = bool] +// CHECK:STDOUT: %.loc4_20.2: type = converted %bool.make_type.loc4_20, %.loc4_20.1 [template = bool] +// CHECK:STDOUT: %bool.make_type.loc4_29: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_29.1: type = value_of_initializer %bool.make_type.loc4_29 [template = bool] +// CHECK:STDOUT: %.loc4_29.2: type = converted %bool.make_type.loc4_29, %.loc4_29.1 [template = bool] +// CHECK:STDOUT: %a.param: bool = value_param runtime_param0 +// CHECK:STDOUT: %a: bool = bind_name a, %a.param +// CHECK:STDOUT: %b.param: bool = value_param runtime_param1 +// CHECK:STDOUT: %b: bool = bind_name b, %b.param +// CHECK:STDOUT: %return.param: ref bool = out_param runtime_param2 +// CHECK:STDOUT: %return: ref bool = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %B.patt.loc6_9.1: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: %B.param_patt: bool = value_param_pattern %B.patt.loc6_9.1, runtime_param [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc6_13.1: type = value_of_initializer %bool.make_type [template = bool] +// CHECK:STDOUT: %.loc6_13.2: type = converted %bool.make_type, %.loc6_13.1 [template = bool] +// CHECK:STDOUT: %B.param: bool = value_param runtime_param +// CHECK:STDOUT: %B.loc6_9.1: bool = bind_symbolic_name B, 0, %B.param [symbolic = %B.loc6_9.2 (constants.%B)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %True.decl: %True.type = fn_decl @True [template = constants.%True] { +// CHECK:STDOUT: %return.patt: %C.2 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.2 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %return.param: ref %C.2 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.2 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %False.decl: %False.type = fn_decl @False [template = constants.%False] { +// CHECK:STDOUT: %return.patt: %C.3 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.3 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %return.param: ref %C.3 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.3 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref.loc11: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Neq.ref.loc11: %Neq.type = name_ref Neq, %Neq.decl [template = constants.%Neq] +// CHECK:STDOUT: %true.loc11_14: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %true.loc11_20: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %bool.neq.loc11: init bool = call %Neq.ref.loc11(%true.loc11_14, %true.loc11_20) [template = constants.%false] +// CHECK:STDOUT: %.loc11_25.1: bool = value_of_initializer %bool.neq.loc11 [template = constants.%false] +// CHECK:STDOUT: %.loc11_25.2: bool = converted %bool.neq.loc11, %.loc11_25.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc11: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %a.var: ref %C.3 = var a +// CHECK:STDOUT: %a: ref %C.3 = bind_name a, %a.var +// CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Neq.ref.loc12: %Neq.type = name_ref Neq, %Neq.decl [template = constants.%Neq] +// CHECK:STDOUT: %true.loc12: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %false.loc12: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %bool.neq.loc12: init bool = call %Neq.ref.loc12(%true.loc12, %false.loc12) [template = constants.%true] +// CHECK:STDOUT: %.loc12_26.1: bool = value_of_initializer %bool.neq.loc12 [template = constants.%true] +// CHECK:STDOUT: %.loc12_26.2: bool = converted %bool.neq.loc12, %.loc12_26.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %b.var: ref %C.2 = var b +// CHECK:STDOUT: %b: ref %C.2 = bind_name b, %b.var +// CHECK:STDOUT: %C.ref.loc13: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Neq.ref.loc13: %Neq.type = name_ref Neq, %Neq.decl [template = constants.%Neq] +// CHECK:STDOUT: %false.loc13: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %true.loc13: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %bool.neq.loc13: init bool = call %Neq.ref.loc13(%false.loc13, %true.loc13) [template = constants.%true] +// CHECK:STDOUT: %.loc13_26.1: bool = value_of_initializer %bool.neq.loc13 [template = constants.%true] +// CHECK:STDOUT: %.loc13_26.2: bool = converted %bool.neq.loc13, %.loc13_26.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc13: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %c.var: ref %C.2 = var c +// CHECK:STDOUT: %c: ref %C.2 = bind_name c, %c.var +// CHECK:STDOUT: %C.ref.loc14: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %Neq.ref.loc14: %Neq.type = name_ref Neq, %Neq.decl [template = constants.%Neq] +// CHECK:STDOUT: %false.loc14_14: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %false.loc14_21: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %bool.neq.loc14: init bool = call %Neq.ref.loc14(%false.loc14_14, %false.loc14_21) [template = constants.%false] +// CHECK:STDOUT: %.loc14_27.1: bool = value_of_initializer %bool.neq.loc14 [template = constants.%false] +// CHECK:STDOUT: %.loc14_27.2: bool = converted %bool.neq.loc14, %.loc14_27.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc14: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %d.var: ref %C.3 = var d +// CHECK:STDOUT: %d: ref %C.3 = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%B.loc6_9.1: bool) { +// CHECK:STDOUT: %B.loc6_9.2: bool = bind_symbolic_name B, 0 [symbolic = %B.loc6_9.2 (constants.%B)] +// CHECK:STDOUT: %B.patt.loc6_9.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc6_9.2 (constants.%B.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Neq(%a.param_patt: bool, %b.param_patt: bool) -> bool = "bool.neq"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @True() -> %C.2; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @False() -> %C.3; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %False.ref.loc11: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc11: ref %C.3 = splice_block file.%a.var {} +// CHECK:STDOUT: %False.call.loc11: init %C.3 = call %False.ref.loc11() to %.loc11 +// CHECK:STDOUT: assign file.%a.var, %False.call.loc11 +// CHECK:STDOUT: %True.ref.loc12: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc12: ref %C.2 = splice_block file.%b.var {} +// CHECK:STDOUT: %True.call.loc12: init %C.2 = call %True.ref.loc12() to %.loc12 +// CHECK:STDOUT: assign file.%b.var, %True.call.loc12 +// CHECK:STDOUT: %True.ref.loc13: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc13: ref %C.2 = splice_block file.%c.var {} +// CHECK:STDOUT: %True.call.loc13: init %C.2 = call %True.ref.loc13() to %.loc13 +// CHECK:STDOUT: assign file.%c.var, %True.call.loc13 +// CHECK:STDOUT: %False.ref.loc14: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc14: ref %C.3 = splice_block file.%d.var {} +// CHECK:STDOUT: %False.call.loc14: init %C.3 = call %False.ref.loc14() to %.loc14 +// CHECK:STDOUT: assign file.%d.var, %False.call.loc14 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%B) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%B +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%B +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%true) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%true +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%true +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%false) { +// CHECK:STDOUT: %B.loc6_9.2 => constants.%false +// CHECK:STDOUT: %B.patt.loc6_9.2 => constants.%false +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- prelude.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [template] +// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [template] +// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic] +// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.1: type = class_type @C, @C(%B) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %true: bool = bool_literal true [template] +// CHECK:STDOUT: %C.2: type = class_type @C, @C(%true) [template] +// CHECK:STDOUT: %True.type: type = fn_type @True [template] +// CHECK:STDOUT: %True: %True.type = struct_value () [template] +// CHECK:STDOUT: %false: bool = bool_literal false [template] +// CHECK:STDOUT: %C.3: type = class_type @C, @C(%false) [template] +// CHECK:STDOUT: %False.type: type = fn_type @False [template] +// CHECK:STDOUT: %False: %False.type = struct_value () [template] +// CHECK:STDOUT: %NotEqual.type.1: type = fn_type @NotEqual.1 [template] +// CHECK:STDOUT: %NotEqual.type.2: type = fn_type @NotEqual.2 [template] +// CHECK:STDOUT: %NotEqual.2: %NotEqual.type.2 = struct_value () [template] +// CHECK:STDOUT: %Equal.type: type = fn_type @Equal [template] +// CHECK:STDOUT: %Equal: %Equal.type = struct_value () [template] +// CHECK:STDOUT: %interface: = interface_witness (%Equal, %NotEqual.2) [template] +// CHECK:STDOUT: %NotEqual.bound.1: = bound_method %true, %NotEqual.2 [template] +// CHECK:STDOUT: %NotEqual.bound.2: = bound_method %false, %NotEqual.2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Bool = %import_ref.1 +// CHECK:STDOUT: .Eq = %import_ref.2 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .True = %True.decl +// CHECK:STDOUT: .False = %False.decl +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %B.patt.loc4_9.1: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: %B.param_patt: bool = value_param_pattern %B.patt.loc4_9.1, runtime_param [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [template = bool] +// CHECK:STDOUT: %.loc4_13.1: type = value_of_initializer %bool.make_type [template = bool] +// CHECK:STDOUT: %.loc4_13.2: type = converted %bool.make_type, %.loc4_13.1 [template = bool] +// CHECK:STDOUT: %B.param: bool = value_param runtime_param +// CHECK:STDOUT: %B.loc4_9.1: bool = bind_symbolic_name B, 0, %B.param [symbolic = %B.loc4_9.2 (constants.%B)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %True.decl: %True.type = fn_decl @True [template = constants.%True] { +// CHECK:STDOUT: %return.patt: %C.2 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.2 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %return.param: ref %C.2 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.2 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %False.decl: %False.type = fn_decl @False [template = constants.%False] { +// CHECK:STDOUT: %return.patt: %C.3 = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C.3 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %C: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %return.param: ref %C.3 = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C.3 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref.loc9: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true.loc9_10: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %true.loc9_18: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %impl.elem1.loc9: %NotEqual.type.1 = interface_witness_access constants.%interface, element1 [template = constants.%NotEqual.2] +// CHECK:STDOUT: %NotEqual.bound.loc9: = bound_method %true.loc9_10, %impl.elem1.loc9 [template = constants.%NotEqual.bound.1] +// CHECK:STDOUT: %bool.neq.loc9: init bool = call %NotEqual.bound.loc9(%true.loc9_10, %true.loc9_18) [template = constants.%false] +// CHECK:STDOUT: %.loc9_22.1: bool = value_of_initializer %bool.neq.loc9 [template = constants.%false] +// CHECK:STDOUT: %.loc9_22.2: bool = converted %bool.neq.loc9, %.loc9_22.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc9: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %a.var: ref %C.3 = var a +// CHECK:STDOUT: %a: ref %C.3 = bind_name a, %a.var +// CHECK:STDOUT: %C.ref.loc10: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %true.loc10: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %false.loc10: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %impl.elem1.loc10: %NotEqual.type.1 = interface_witness_access constants.%interface, element1 [template = constants.%NotEqual.2] +// CHECK:STDOUT: %NotEqual.bound.loc10: = bound_method %true.loc10, %impl.elem1.loc10 [template = constants.%NotEqual.bound.1] +// CHECK:STDOUT: %bool.neq.loc10: init bool = call %NotEqual.bound.loc10(%true.loc10, %false.loc10) [template = constants.%true] +// CHECK:STDOUT: %.loc10_23.1: bool = value_of_initializer %bool.neq.loc10 [template = constants.%true] +// CHECK:STDOUT: %.loc10_23.2: bool = converted %bool.neq.loc10, %.loc10_23.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc10: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %b.var: ref %C.2 = var b +// CHECK:STDOUT: %b: ref %C.2 = bind_name b, %b.var +// CHECK:STDOUT: %C.ref.loc11: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false.loc11: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %true.loc11: bool = bool_literal true [template = constants.%true] +// CHECK:STDOUT: %impl.elem1.loc11: %NotEqual.type.1 = interface_witness_access constants.%interface, element1 [template = constants.%NotEqual.2] +// CHECK:STDOUT: %NotEqual.bound.loc11: = bound_method %false.loc11, %impl.elem1.loc11 [template = constants.%NotEqual.bound.2] +// CHECK:STDOUT: %bool.neq.loc11: init bool = call %NotEqual.bound.loc11(%false.loc11, %true.loc11) [template = constants.%true] +// CHECK:STDOUT: %.loc11_23.1: bool = value_of_initializer %bool.neq.loc11 [template = constants.%true] +// CHECK:STDOUT: %.loc11_23.2: bool = converted %bool.neq.loc11, %.loc11_23.1 [template = constants.%true] +// CHECK:STDOUT: %C.loc11: type = class_type @C, @C(constants.%true) [template = constants.%C.2] +// CHECK:STDOUT: %c.var: ref %C.2 = var c +// CHECK:STDOUT: %c: ref %C.2 = bind_name c, %c.var +// CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [template = constants.%C.generic] +// CHECK:STDOUT: %false.loc12_10: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %false.loc12_19: bool = bool_literal false [template = constants.%false] +// CHECK:STDOUT: %impl.elem1.loc12: %NotEqual.type.1 = interface_witness_access constants.%interface, element1 [template = constants.%NotEqual.2] +// CHECK:STDOUT: %NotEqual.bound.loc12: = bound_method %false.loc12_10, %impl.elem1.loc12 [template = constants.%NotEqual.bound.2] +// CHECK:STDOUT: %bool.neq.loc12: init bool = call %NotEqual.bound.loc12(%false.loc12_10, %false.loc12_19) [template = constants.%false] +// CHECK:STDOUT: %.loc12_24.1: bool = value_of_initializer %bool.neq.loc12 [template = constants.%false] +// CHECK:STDOUT: %.loc12_24.2: bool = converted %bool.neq.loc12, %.loc12_24.1 [template = constants.%false] +// CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%false) [template = constants.%C.3] +// CHECK:STDOUT: %d.var: ref %C.3 = var d +// CHECK:STDOUT: %d: ref %C.3 = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%B.loc4_9.1: bool) { +// CHECK:STDOUT: %B.loc4_9.2: bool = bind_symbolic_name B, 0 [symbolic = %B.loc4_9.2 (constants.%B)] +// CHECK:STDOUT: %B.patt.loc4_9.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_9.2 (constants.%B.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @True() -> %C.2; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @False() -> %C.3; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %False.ref.loc9: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc9: ref %C.3 = splice_block file.%a.var {} +// CHECK:STDOUT: %False.call.loc9: init %C.3 = call %False.ref.loc9() to %.loc9 +// CHECK:STDOUT: assign file.%a.var, %False.call.loc9 +// CHECK:STDOUT: %True.ref.loc10: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc10: ref %C.2 = splice_block file.%b.var {} +// CHECK:STDOUT: %True.call.loc10: init %C.2 = call %True.ref.loc10() to %.loc10 +// CHECK:STDOUT: assign file.%b.var, %True.call.loc10 +// CHECK:STDOUT: %True.ref.loc11: %True.type = name_ref True, file.%True.decl [template = constants.%True] +// CHECK:STDOUT: %.loc11: ref %C.2 = splice_block file.%c.var {} +// CHECK:STDOUT: %True.call.loc11: init %C.2 = call %True.ref.loc11() to %.loc11 +// CHECK:STDOUT: assign file.%c.var, %True.call.loc11 +// CHECK:STDOUT: %False.ref.loc12: %False.type = name_ref False, file.%False.decl [template = constants.%False] +// CHECK:STDOUT: %.loc12: ref %C.3 = splice_block file.%d.var {} +// CHECK:STDOUT: %False.call.loc12: init %C.3 = call %False.ref.loc12() to %.loc12 +// CHECK:STDOUT: assign file.%d.var, %False.call.loc12 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%B) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%B +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%B +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%true) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%true +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%true +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%false) { +// CHECK:STDOUT: %B.loc4_9.2 => constants.%false +// CHECK:STDOUT: %B.patt.loc4_9.2 => constants.%false +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/eq.carbon b/toolchain/check/testdata/operators/overloaded/eq.carbon index 8b3ce73429e5e..aba611ffadaef 100644 --- a/toolchain/check/testdata/operators/overloaded/eq.carbon +++ b/toolchain/check/testdata/operators/overloaded/eq.carbon @@ -123,7 +123,7 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} -// CHECK:STDOUT: impl_decl @impl [template] {} { +// CHECK:STDOUT: impl_decl @impl.1 [template] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] // CHECK:STDOUT: %Eq.ref: type = name_ref Eq, imports.%import_ref.1 [template = constants.%Eq.type] @@ -170,7 +170,7 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Eq.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Eq.ref { // CHECK:STDOUT: %Equal.decl: %Equal.type.1 = fn_decl @Equal.1 [template = constants.%Equal.1] { // CHECK:STDOUT: %self.patt: %C = binding_pattern self // CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, runtime_param0 @@ -377,7 +377,7 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Eq = %import_ref.1 // CHECK:STDOUT: .Bool = %import_ref.7 -// CHECK:STDOUT: .ImplicitAs = %import_ref.9 +// CHECK:STDOUT: .ImplicitAs = %import_ref.12 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -395,7 +395,7 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [template = constants.%D] {} {} -// CHECK:STDOUT: impl_decl @impl [template] {} { +// CHECK:STDOUT: impl_decl @impl.1 [template] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] // CHECK:STDOUT: %Eq.ref: type = name_ref Eq, imports.%import_ref.1 [template = constants.%Eq.type] @@ -442,7 +442,7 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Eq.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Eq.ref { // CHECK:STDOUT: %Equal.decl: %Equal.type.1 = fn_decl @Equal.1 [template = constants.%Equal.1] { // CHECK:STDOUT: %self.patt: %C = binding_pattern self // CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, runtime_param0 diff --git a/toolchain/check/testdata/operators/overloaded/ordered.carbon b/toolchain/check/testdata/operators/overloaded/ordered.carbon index b65479c0edc70..25ca32ecca90a 100644 --- a/toolchain/check/testdata/operators/overloaded/ordered.carbon +++ b/toolchain/check/testdata/operators/overloaded/ordered.carbon @@ -127,7 +127,7 @@ fn TestGreaterEqual(a: D, b: D) -> bool { // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} -// CHECK:STDOUT: impl_decl @impl [template] {} { +// CHECK:STDOUT: impl_decl @impl.1 [template] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] // CHECK:STDOUT: %Ordered.ref: type = name_ref Ordered, imports.%import_ref.1 [template = constants.%Ordered.type] @@ -214,7 +214,7 @@ fn TestGreaterEqual(a: D, b: D) -> bool { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Ordered.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Ordered.ref { // CHECK:STDOUT: %Less.decl: %Less.type.1 = fn_decl @Less.1 [template = constants.%Less.1] { // CHECK:STDOUT: %self.patt: %C = binding_pattern self // CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, runtime_param0 diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index f3af55cb769c4..9ea2dc5e6b83d 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -18,8 +18,10 @@ static auto GetBuiltinICmpPredicate(SemIR::BuiltinFunctionKind builtin_kind, -> llvm::CmpInst::Predicate { switch (builtin_kind) { case SemIR::BuiltinFunctionKind::IntEq: + case SemIR::BuiltinFunctionKind::BoolEq: return llvm::CmpInst::ICMP_EQ; case SemIR::BuiltinFunctionKind::IntNeq: + case SemIR::BuiltinFunctionKind::BoolNeq: return llvm::CmpInst::ICMP_NE; case SemIR::BuiltinFunctionKind::IntLess: return is_signed ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT; @@ -263,7 +265,9 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::IntLess: case SemIR::BuiltinFunctionKind::IntLessEq: case SemIR::BuiltinFunctionKind::IntGreater: - case SemIR::BuiltinFunctionKind::IntGreaterEq: { + case SemIR::BuiltinFunctionKind::IntGreaterEq: + case SemIR::BuiltinFunctionKind::BoolEq: + case SemIR::BuiltinFunctionKind::BoolNeq: { context.SetLocal( inst_id, context.builder().CreateICmp( diff --git a/toolchain/lower/testdata/builtins/no_prelude/bool.carbon b/toolchain/lower/testdata/builtins/no_prelude/bool.carbon new file mode 100644 index 0000000000000..1b8b158f9464e --- /dev/null +++ b/toolchain/lower/testdata/builtins/no_prelude/bool.carbon @@ -0,0 +1,70 @@ +// 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 +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/builtins/no_prelude/bool.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/no_prelude/bool.carbon + +fn Bool() -> type = "bool.make_type"; + +fn Eq(a: Bool(), b: Bool()) -> Bool() = "bool.eq"; +fn TestEq(a: Bool(), b: Bool()) -> Bool() { return Eq(a, b); } + +fn Neq(a: Bool(), b: Bool()) -> Bool() = "bool.neq"; +fn TestNeq(a: Bool(), b: Bool()) -> Bool() { return Neq(a, b); } + +fn IfEq(a: Bool(), b: Bool()) -> Bool() { + if (Eq(a, b)) { return true; } + return false; +} + +// CHECK:STDOUT: ; ModuleID = 'bool.carbon' +// CHECK:STDOUT: source_filename = "bool.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq.Main(i1 %a, i1 %b) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %bool.eq = icmp eq i1 %a, %b, !dbg !7 +// CHECK:STDOUT: ret i1 %bool.eq, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestNeq.Main(i1 %a, i1 %b) !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %bool.neq = icmp ne i1 %a, %b, !dbg !10 +// CHECK:STDOUT: ret i1 %bool.neq, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CIfEq.Main(i1 %a, i1 %b) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %bool.eq = icmp eq i1 %a, %b, !dbg !13 +// CHECK:STDOUT: br i1 %bool.eq, label %if.then, label %if.else, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %entry +// CHECK:STDOUT: ret i1 true, !dbg !15 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %entry +// CHECK:STDOUT: ret i1 false, !dbg !16 +// CHECK:STDOUT: } +// 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, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "bool.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestEq", linkageName: "_CTestEq.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 14, column: 52, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 45, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestNeq", linkageName: "_CTestNeq.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 17, column: 53, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 17, column: 46, scope: !9) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "IfEq", linkageName: "_CIfEq.Main", scope: null, file: !3, line: 19, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 20, column: 7, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 20, column: 6, scope: !12) +// CHECK:STDOUT: !15 = !DILocation(line: 20, column: 19, scope: !12) +// CHECK:STDOUT: !16 = !DILocation(line: 21, column: 3, scope: !12) diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 915c9cc9d6af2..3146f6c55f8f0 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -372,6 +372,14 @@ constexpr BuiltinInfo FloatGreater = { constexpr BuiltinInfo FloatGreaterEq = { "float.greater_eq", ValidateSignatureBool>}; +// "bool.eq": bool equality comparison. +constexpr BuiltinInfo BoolEq = {"bool.eq", + ValidateSignatureBool>}; + +// "bool.neq": bool non-equality comparison. +constexpr BuiltinInfo BoolNeq = {"bool.neq", + ValidateSignatureBool>}; + } // namespace BuiltinFunctionInfo CARBON_DEFINE_ENUM_CLASS_NAMES(BuiltinFunctionKind) = { diff --git a/toolchain/sem_ir/builtin_function_kind.def b/toolchain/sem_ir/builtin_function_kind.def index d74d755da45f2..0b643707cd976 100644 --- a/toolchain/sem_ir/builtin_function_kind.def +++ b/toolchain/sem_ir/builtin_function_kind.def @@ -78,4 +78,8 @@ CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatLessEq) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatGreater) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatGreaterEq) +// Bool comparison. +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolEq) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolNeq) + #undef CARBON_SEM_IR_BUILTIN_FUNCTION_KIND