diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 15447649e1b9..909ecc6d9755 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -286,8 +286,134 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst) return context.constant_values().Get(elements[index_val.getZExtValue()]); } +// Issues a diagnostic for a compile-time division by zero. +static auto DiagnoseDivisionByZero(Context& context, SemIRLocation loc) + -> void { + CARBON_DIAGNOSTIC(CompileTimeDivisionByZero, Error, "Division by zero."); + context.emitter().Emit(loc, CompileTimeDivisionByZero); +} + +// Performs a builtin unary integer -> integer operation. +static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLocation loc, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::InstId arg_id) + -> SemIR::ConstantId { + CARBON_CHECK(builtin_kind == SemIR::BuiltinFunctionKind::IntNegate) + << "Unexpected builtin kind"; + + auto op = context.insts().GetAs(arg_id); + auto op_val = context.ints().Get(op.int_id); + + if (op_val.isMinSignedValue()) { + CARBON_DIAGNOSTIC(CompileTimeIntegerNegateOverflow, Error, + "Integer overflow in negation of {0}.", llvm::APSInt); + context.emitter().Emit(loc, CompileTimeIntegerNegateOverflow, + llvm::APSInt(op_val, false)); + } + op_val.negate(); + + auto result = context.ints().Add(op_val); + return MakeConstantResult(context, SemIR::IntLiteral{op.type_id, result}, + Phase::Template); +} + +// Performs a builtin binary integer -> integer operation. +static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLocation loc, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::InstId lhs_id, + SemIR::InstId rhs_id) + -> SemIR::ConstantId { + auto lhs = context.insts().GetAs(lhs_id); + auto rhs = context.insts().GetAs(rhs_id); + auto lhs_val = context.ints().Get(lhs.int_id); + auto rhs_val = context.ints().Get(rhs.int_id); + + bool overflow = false; + llvm::APInt result_val; + llvm::StringLiteral op_str = ""; + switch (builtin_kind) { + case SemIR::BuiltinFunctionKind::IntAdd: + result_val = lhs_val.sadd_ov(rhs_val, overflow); + op_str = "+"; + break; + case SemIR::BuiltinFunctionKind::IntSub: + result_val = lhs_val.ssub_ov(rhs_val, overflow); + op_str = "-"; + break; + case SemIR::BuiltinFunctionKind::IntMul: + result_val = lhs_val.smul_ov(rhs_val, overflow); + op_str = "*"; + break; + case SemIR::BuiltinFunctionKind::IntDiv: + if (rhs_val.isZero()) { + DiagnoseDivisionByZero(context, loc); + return SemIR::ConstantId::Error; + } + result_val = lhs_val.sdiv_ov(rhs_val, overflow); + op_str = "/"; + break; + case SemIR::BuiltinFunctionKind::IntMod: + if (rhs_val.isZero()) { + DiagnoseDivisionByZero(context, loc); + return SemIR::ConstantId::Error; + } + result_val = lhs_val.srem(rhs_val); + // LLVM weirdly lacks `srem_ov`, so we work it out for ourselves: + // % -1 overflows because / -1 overflows. + overflow = (lhs_val.isMinSignedValue() && rhs_val.isAllOnes()); + op_str = "%"; + break; + + default: + CARBON_FATAL() << "Unexpected operation kind."; + } + + if (overflow) { + CARBON_DIAGNOSTIC(CompileTimeIntegerOverflow, Error, + "Integer overflow in calculation {0} {1} {2}.", + llvm::APSInt, llvm::StringLiteral, llvm::APSInt); + context.emitter().Emit(loc, CompileTimeIntegerOverflow, + llvm::APSInt(lhs_val, false), op_str, + llvm::APSInt(rhs_val, false)); + } + + auto result = context.ints().Add(result_val); + return MakeConstantResult(context, SemIR::IntLiteral{lhs.type_id, result}, + Phase::Template); +} + +// Performs a builtin integer comparison. +static auto PerformBuiltinIntComparison(Context& context, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::InstId lhs_id, + SemIR::InstId rhs_id, + SemIR::TypeId bool_type_id) + -> SemIR::ConstantId { + auto lhs_val = context.ints().Get( + context.insts().GetAs(lhs_id).int_id); + auto rhs_val = context.ints().Get( + context.insts().GetAs(rhs_id).int_id); + + bool result; + switch (builtin_kind) { + case SemIR::BuiltinFunctionKind::IntEq: + result = (lhs_val == rhs_val); + break; + case SemIR::BuiltinFunctionKind::IntNeq: + result = (lhs_val != rhs_val); + break; + default: + CARBON_FATAL() << "Unexpected operation kind."; + } + + return MakeConstantResult( + context, + SemIR::BoolLiteral{bool_type_id, SemIR::BoolValue::FromBool(result)}, + Phase::Template); +} + static auto PerformBuiltinCall(Context& context, SemIRLocation loc, - SemIR::Call /*call*/, + SemIR::Call call, SemIR::BuiltinFunctionKind builtin_kind, llvm::ArrayRef arg_ids, Phase phase) -> SemIR::ConstantId { @@ -295,28 +421,38 @@ static auto PerformBuiltinCall(Context& context, SemIRLocation loc, case SemIR::BuiltinFunctionKind::None: CARBON_FATAL() << "Not a builtin function."; - case SemIR::BuiltinFunctionKind::IntAdd: { + // Unary integer -> integer operations. + case SemIR::BuiltinFunctionKind::IntNegate: { + // TODO: Complement. if (phase != Phase::Template) { break; } - auto lhs = context.insts().GetAs(arg_ids[0]); - auto rhs = context.insts().GetAs(arg_ids[1]); - // TODO: Integer values should be stored in the correct bit width for - // their types. For now we assume i32. - auto lhs_val = context.ints().Get(lhs.int_id).sextOrTrunc(32); - auto rhs_val = context.ints().Get(rhs.int_id).sextOrTrunc(32); - bool overflow = false; - auto result = context.ints().Add(lhs_val.sadd_ov(rhs_val, overflow)); - if (overflow) { - CARBON_DIAGNOSTIC(CompileTimeIntegerOverflow, Error, - "Integer overflow in calculation {0} + {1}.", - llvm::APSInt, llvm::APSInt); - context.emitter().Emit(loc, CompileTimeIntegerOverflow, - llvm::APSInt(lhs_val, false), - llvm::APSInt(rhs_val, false)); + return PerformBuiltinUnaryIntOp(context, loc, builtin_kind, arg_ids[0]); + } + + // Homogeneous binary integer -> integer operations. + case SemIR::BuiltinFunctionKind::IntAdd: + case SemIR::BuiltinFunctionKind::IntSub: + case SemIR::BuiltinFunctionKind::IntMul: + case SemIR::BuiltinFunctionKind::IntDiv: + case SemIR::BuiltinFunctionKind::IntMod: { + // TODO: Bitwise operators. + if (phase != Phase::Template) { + break; } - return MakeConstantResult(context, SemIR::IntLiteral{lhs.type_id, result}, - phase); + return PerformBuiltinBinaryIntOp(context, loc, builtin_kind, arg_ids[0], + arg_ids[1]); + } + + // Integer comparisons. + case SemIR::BuiltinFunctionKind::IntEq: + case SemIR::BuiltinFunctionKind::IntNeq: { + // TODO: Relational comparisons. + if (phase != Phase::Template) { + break; + } + return PerformBuiltinIntComparison(context, builtin_kind, arg_ids[0], + arg_ids[1], call.type_id); } } @@ -383,9 +519,17 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // TODO: We should check that the size of the resulting array type // fits in 64 bits, not just that the bound does. Should we use a // 32-bit limit for 32-bit targets? - // TODO: Also check for a negative bound, once that's something we - // can represent. const auto& bound_val = context.ints().Get(int_bound->int_id); + if (bound_val.isNegative()) { + // TODO: Skip this test if the bound type is unsigned. + CARBON_DIAGNOSTIC(ArrayBoundNegative, Error, + "Array bound of {0} is negative.", + llvm::APSInt); + context.emitter().Emit( + bound_id, ArrayBoundNegative, + llvm::APSInt(bound_val, /*isUnsigned=*/false)); + return false; + } if (bound_val.getActiveBits() > 64) { CARBON_DIAGNOSTIC(ArrayBoundTooLarge, Error, "Array bound of {0} is too large.", diff --git a/toolchain/check/testdata/array/fail_bound_negative.carbon b/toolchain/check/testdata/array/fail_bound_negative.carbon index c4fdb8202a08..04825ab0877b 100644 --- a/toolchain/check/testdata/array/fail_bound_negative.carbon +++ b/toolchain/check/testdata/array/fail_bound_negative.carbon @@ -4,27 +4,37 @@ // // AUTOUPDATE -// TODO: Once we supoprt negating integer literals, check that we reject a -// negative array bound. +fn Negate(n: i32) -> i32 = "int.negate"; -// CHECK:STDERR: fail_bound_negative.carbon:[[@LINE+3]]:14: ERROR: Semantics TODO: `missing or invalid operator interface`. -// CHECK:STDERR: var a: [i32; -1]; -// CHECK:STDERR: ^~ -var a: [i32; -1]; +// CHECK:STDERR: fail_bound_negative.carbon:[[@LINE+3]]:14: ERROR: Array bound of -1 is negative. +// CHECK:STDERR: var a: [i32; Negate(1)]; +// CHECK:STDERR: ^~~~~~~ +var a: [i32; Negate(1)]; // CHECK:STDOUT: --- fail_bound_negative.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 4294967295 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Negate = %Negate // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } -// CHECK:STDOUT: %.loc13_15: i32 = int_literal 1 [template = constants.%.1] -// CHECK:STDOUT: %.loc13_16: type = array_type , i32 [template = ] +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %n.loc7_11.1: i32 = param n +// CHECK:STDOUT: @Negate.%n: i32 = bind_name n, %n.loc7_11.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate.ref: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc12_21: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_20: init i32 = call %Negate.ref(%.loc12_21) [template = constants.%.2] +// CHECK:STDOUT: %.loc12_23: type = array_type %.loc12_20, i32 [template = ] // CHECK:STDOUT: %a.var: ref = var a // CHECK:STDOUT: %a: ref = bind_name a, %a.var // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%n: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_add.carbon b/toolchain/check/testdata/builtins/int_add.carbon index f9f218055a1b..3518b83965d3 100644 --- a/toolchain/check/testdata/builtins/int_add.carbon +++ b/toolchain/check/testdata/builtins/int_add.carbon @@ -9,6 +9,7 @@ fn Add(a: i32, b: i32) -> i32 = "int.add"; var arr: [i32; Add(1, 2)]; +let arr_p: [i32; 3]* = &arr; fn RuntimeCall(a: i32, b: i32) -> i32 { return Add(a, b); @@ -114,12 +115,18 @@ let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] // CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr // CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 3 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.4] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 3] [template = constants.%.5] +// CHECK:STDOUT: %arr.ref: ref [i32; 3] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 3]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 3]* = bind_name arr_p, %.loc5_24 // CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { -// CHECK:STDOUT: %a.loc6_16.1: i32 = param a -// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc6_16.1 -// CHECK:STDOUT: %b.loc6_24.1: i32 = param b -// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc6_24.1 -// CHECK:STDOUT: %return.var.loc6: ref i32 = var +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -130,10 +137,10 @@ let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDOUT: %Add.ref: = name_ref Add, file.%Add [template = file.%Add] // CHECK:STDOUT: %a.ref: i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: i32 = name_ref b, %b -// CHECK:STDOUT: %.loc7_13.1: init i32 = call %Add.ref(%a.ref, %b.ref) -// CHECK:STDOUT: %.loc7_19: i32 = value_of_initializer %.loc7_13.1 -// CHECK:STDOUT: %.loc7_13.2: i32 = converted %.loc7_13.1, %.loc7_19 -// CHECK:STDOUT: return %.loc7_13.2 +// CHECK:STDOUT: %.loc8_13.1: init i32 = call %Add.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc8_19: i32 = value_of_initializer %.loc8_13.1 +// CHECK:STDOUT: %.loc8_13.2: i32 = converted %.loc8_13.1, %.loc8_19 +// CHECK:STDOUT: return %.loc8_13.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_bad_decl.carbon diff --git a/toolchain/check/testdata/builtins/int_div.carbon b/toolchain/check/testdata/builtins/int_div.carbon new file mode 100644 index 000000000000..4ec35921cd76 --- /dev/null +++ b/toolchain/check/testdata/builtins/int_div.carbon @@ -0,0 +1,243 @@ +// 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 + +// --- int_div.carbon + +fn Div(a: i32, b: i32) -> i32 = "int.div"; + +var arr: [i32; Div(3, 2)]; +let arr_p: [i32; 1]* = &arr; + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Div(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Div(a: i32, b: i32) -> i32 = "int.div"; +fn Sub(a: i32, b: i32) -> i32 = "int.sub"; +fn Negate(a: i32) -> i32 = "int.negate"; + +// -0x7FFF_FFFF / -1 is OK. +let a: i32 = Div(Negate(0x7FFF_FFFF), Negate(1)); + +// -0x8000_0000 / 1 is OK. +let b: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), 1); + +// -0x8000_0000 / -1 overflows. +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: ERROR: Integer overflow in calculation -2147483648 / -1. +// CHECK:STDERR: let c: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +let c: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); + +// --- fail_div_by_zero.carbon + +package FailDivByZero api; + +fn Div(a: i32, b: i32) -> i32 = "int.div"; + +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: ERROR: Division by zero. +// CHECK:STDERR: let a: i32 = Div(1, 0); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +let a: i32 = Div(1, 0); + +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+3]]:14: ERROR: Division by zero. +// CHECK:STDERR: let b: i32 = Div(0, 0); +// CHECK:STDERR: ^~~~ +let b: i32 = Div(0, 0); + +// CHECK:STDOUT: --- int_div.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 1] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Div = %Div +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div: = fn_decl @Div [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Div.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Div.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div.ref: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %.loc4_20: i32 = int_literal 3 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_23: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_19: init i32 = call %Div.ref(%.loc4_20, %.loc4_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 1] = var arr +// CHECK:STDOUT: %arr: ref [i32; 1] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.4] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 1] [template = constants.%.5] +// CHECK:STDOUT: %arr.ref: ref [i32; 1] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 1]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 1]* = bind_name arr_p, %.loc5_24 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Div(%a: i32, %b: i32) -> i32 = "int.div"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Div.ref: = name_ref Div, file.%Div [template = file.%Div] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc8_13.1: init i32 = call %Div.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc8_19: i32 = value_of_initializer %.loc8_13.1 +// CHECK:STDOUT: %.loc8_13.2: i32 = converted %.loc8_13.1, %.loc8_19 +// CHECK:STDOUT: return %.loc8_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 2147483647 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2147483649 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 4294967295 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Div = %Div +// CHECK:STDOUT: .Sub = %Sub +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div: = fn_decl @Div [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Div.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Div.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var.loc4: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub: = fn_decl @Sub [template] { +// CHECK:STDOUT: %a.loc5_8.1: i32 = param a +// CHECK:STDOUT: @Sub.%a: i32 = bind_name a, %a.loc5_8.1 +// CHECK:STDOUT: %b.loc5_16.1: i32 = param b +// CHECK:STDOUT: @Sub.%b: i32 = bind_name b, %b.loc5_16.1 +// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %a.loc6_11.1: i32 = param a +// CHECK:STDOUT: @Negate.%a: i32 = bind_name a, %a.loc6_11.1 +// CHECK:STDOUT: %return.var.loc6: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div.ref.loc9: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %Negate.ref.loc9_18: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc9_25: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_24.1: init i32 = call %Negate.ref.loc9_18(%.loc9_25) [template = constants.%.2] +// CHECK:STDOUT: %Negate.ref.loc9_39: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc9_46: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc9_45.1: init i32 = call %Negate.ref.loc9_39(%.loc9_46) [template = constants.%.4] +// CHECK:STDOUT: %.loc9_17.1: i32 = value_of_initializer %.loc9_24.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc9_24.2: i32 = converted %.loc9_24.1, %.loc9_17.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc9_17.2: i32 = value_of_initializer %.loc9_45.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_45.2: i32 = converted %.loc9_45.1, %.loc9_17.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_17.3: init i32 = call %Div.ref.loc9(%.loc9_24.2, %.loc9_45.2) [template = constants.%.1] +// CHECK:STDOUT: %.loc9_49: i32 = value_of_initializer %.loc9_17.3 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_17.4: i32 = converted %.loc9_17.3, %.loc9_49 [template = constants.%.1] +// CHECK:STDOUT: %a.loc9: i32 = bind_name a, %.loc9_17.4 +// CHECK:STDOUT: %Div.ref.loc12: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %Sub.ref.loc12: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Negate.ref.loc12: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc12_29: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_28.1: init i32 = call %Negate.ref.loc12(%.loc12_29) [template = constants.%.2] +// CHECK:STDOUT: %.loc12_43: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc12_21.1: i32 = value_of_initializer %.loc12_28.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_28.2: i32 = converted %.loc12_28.1, %.loc12_21.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_21.2: init i32 = call %Sub.ref.loc12(%.loc12_28.2, %.loc12_43) [template = constants.%.5] +// CHECK:STDOUT: %.loc12_47: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc12_17.1: i32 = value_of_initializer %.loc12_21.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_21.3: i32 = converted %.loc12_21.2, %.loc12_17.1 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_17.2: init i32 = call %Div.ref.loc12(%.loc12_21.3, %.loc12_47) [template = constants.%.5] +// CHECK:STDOUT: %.loc12_49: i32 = value_of_initializer %.loc12_17.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_17.3: i32 = converted %.loc12_17.2, %.loc12_49 [template = constants.%.5] +// CHECK:STDOUT: %b.loc12: i32 = bind_name b, %.loc12_17.3 +// CHECK:STDOUT: %Div.ref.loc19: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %Sub.ref.loc19: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Negate.ref.loc19_22: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc19_29: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc19_28.1: init i32 = call %Negate.ref.loc19_22(%.loc19_29) [template = constants.%.2] +// CHECK:STDOUT: %.loc19_43: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc19_21.1: i32 = value_of_initializer %.loc19_28.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc19_28.2: i32 = converted %.loc19_28.1, %.loc19_21.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc19_21.2: init i32 = call %Sub.ref.loc19(%.loc19_28.2, %.loc19_43) [template = constants.%.5] +// CHECK:STDOUT: %Negate.ref.loc19_47: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc19_54: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc19_53.1: init i32 = call %Negate.ref.loc19_47(%.loc19_54) [template = constants.%.4] +// CHECK:STDOUT: %.loc19_17.1: i32 = value_of_initializer %.loc19_21.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc19_21.3: i32 = converted %.loc19_21.2, %.loc19_17.1 [template = constants.%.5] +// CHECK:STDOUT: %.loc19_17.2: i32 = value_of_initializer %.loc19_53.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc19_53.2: i32 = converted %.loc19_53.1, %.loc19_17.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc19_17.3: init i32 = call %Div.ref.loc19(%.loc19_21.3, %.loc19_53.2) [template = constants.%.5] +// CHECK:STDOUT: %.loc19_57: i32 = value_of_initializer %.loc19_17.3 [template = constants.%.5] +// CHECK:STDOUT: %.loc19_17.4: i32 = converted %.loc19_17.3, %.loc19_57 [template = constants.%.5] +// CHECK:STDOUT: %c: i32 = bind_name c, %.loc19_17.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Div(%a: i32, %b: i32) -> i32 = "int.div"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Sub(%a: i32, %b: i32) -> i32 = "int.sub"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%a: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_div_by_zero.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Div = %Div +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div: = fn_decl @Div [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Div.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Div.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Div.ref.loc10: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %.loc10_18: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_21: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc10_17.1: init i32 = call %Div.ref.loc10(%.loc10_18, %.loc10_21) [template = ] +// CHECK:STDOUT: %.loc10_23: i32 = value_of_initializer %.loc10_17.1 [template = ] +// CHECK:STDOUT: %.loc10_17.2: i32 = converted %.loc10_17.1, %.loc10_23 [template = ] +// CHECK:STDOUT: %a.loc10: i32 = bind_name a, %.loc10_17.2 +// CHECK:STDOUT: %Div.ref.loc15: = name_ref Div, %Div [template = %Div] +// CHECK:STDOUT: %.loc15_18: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc15_21: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc15_17.1: init i32 = call %Div.ref.loc15(%.loc15_18, %.loc15_21) [template = ] +// CHECK:STDOUT: %.loc15_23: i32 = value_of_initializer %.loc15_17.1 [template = ] +// CHECK:STDOUT: %.loc15_17.2: i32 = converted %.loc15_17.1, %.loc15_23 [template = ] +// CHECK:STDOUT: %b.loc15: i32 = bind_name b, %.loc15_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Div(%a: i32, %b: i32) -> i32 = "int.div"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_eq.carbon b/toolchain/check/testdata/builtins/int_eq.carbon new file mode 100644 index 000000000000..00a2a740bba0 --- /dev/null +++ b/toolchain/check/testdata/builtins/int_eq.carbon @@ -0,0 +1,101 @@ +// 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 + +// --- int_eq.carbon + +fn Eq(a: i32, b: i32) -> bool = "int.eq"; + +// TODO: Use a test that will fail to compile if we get the wrong value. +let b_true: bool = Eq(1, 1); +let b_false: bool = Eq(1, 2); + +fn RuntimeCall(a: i32, b: i32) -> bool { + return Eq(a, b); +} + +// --- fail_bad_decl.carbon + +package FailBadDecl api; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+3]]:1: ERROR: Invalid signature for builtin function "int.eq". +// CHECK:STDERR: fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; + +// CHECK:STDOUT: --- int_eq.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: bool = bool_literal true [template] +// CHECK:STDOUT: %.3: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.4: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Eq = %Eq +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Eq: = fn_decl @Eq [template] { +// CHECK:STDOUT: %a.loc2_7.1: i32 = param a +// CHECK:STDOUT: @Eq.%a: i32 = bind_name a, %a.loc2_7.1 +// CHECK:STDOUT: %b.loc2_15.1: i32 = param b +// CHECK:STDOUT: @Eq.%b: i32 = bind_name b, %b.loc2_15.1 +// CHECK:STDOUT: %return.var.loc2: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Eq.ref.loc5: = name_ref Eq, %Eq [template = %Eq] +// CHECK:STDOUT: %.loc5_23: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_26: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_22.1: init bool = call %Eq.ref.loc5(%.loc5_23, %.loc5_26) [template = constants.%.2] +// CHECK:STDOUT: %.loc5_28: bool = value_of_initializer %.loc5_22.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc5_22.2: bool = converted %.loc5_22.1, %.loc5_28 [template = constants.%.2] +// CHECK:STDOUT: %b_true: bool = bind_name b_true, %.loc5_22.2 +// CHECK:STDOUT: %Eq.ref.loc6: = name_ref Eq, %Eq [template = %Eq] +// CHECK:STDOUT: %.loc6_24: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_27: i32 = int_literal 2 [template = constants.%.3] +// CHECK:STDOUT: %.loc6_23.1: init bool = call %Eq.ref.loc6(%.loc6_24, %.loc6_27) [template = constants.%.4] +// CHECK:STDOUT: %.loc6_29: bool = value_of_initializer %.loc6_23.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc6_23.2: bool = converted %.loc6_23.1, %.loc6_29 [template = constants.%.4] +// CHECK:STDOUT: %b_false: bool = bind_name b_false, %.loc6_23.2 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc8_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc8_16.1 +// CHECK:STDOUT: %b.loc8_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc8_24.1 +// CHECK:STDOUT: %return.var.loc8: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Eq(%a: i32, %b: i32) -> bool = "int.eq"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Eq.ref: = name_ref Eq, file.%Eq [template = file.%Eq] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc9_12.1: init bool = call %Eq.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc9_18: bool = value_of_initializer %.loc9_12.1 +// CHECK:STDOUT: %.loc9_12.2: bool = converted %.loc9_12.1, %.loc9_18 +// CHECK:STDOUT: return %.loc9_12.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_bad_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .WrongResult = %WrongResult +// CHECK:STDOUT: } +// CHECK:STDOUT: %WrongResult: = fn_decl @WrongResult [template] { +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @WrongResult.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @WrongResult.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @WrongResult(%a: i32, %b: i32) -> i32; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_mod.carbon b/toolchain/check/testdata/builtins/int_mod.carbon new file mode 100644 index 000000000000..23ea91e6400d --- /dev/null +++ b/toolchain/check/testdata/builtins/int_mod.carbon @@ -0,0 +1,247 @@ +// 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 + +// --- int_div.carbon + +fn Mod(a: i32, b: i32) -> i32 = "int.mod"; + +var arr: [i32; Mod(5, 3)]; +let arr_p: [i32; 2]* = &arr; + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Mod(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Mod(a: i32, b: i32) -> i32 = "int.mod"; +fn Sub(a: i32, b: i32) -> i32 = "int.sub"; +fn Negate(a: i32) -> i32 = "int.negate"; + +// -0x7FFF_FFFF % -1 is OK. +let a: i32 = Mod(Negate(0x7FFF_FFFF), Negate(1)); + +// -0x8000_0000 % 1 is OK. +let b: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), 1); + +// -0x8000_0000 / -1 overflows, so -0x8000_0000 % -1 is disallowed, even though +// its result is representable. +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: ERROR: Integer overflow in calculation -2147483648 % -1. +// CHECK:STDERR: let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); + +// --- fail_div_by_zero.carbon + +package FailDivByZero api; + +fn Mod(a: i32, b: i32) -> i32 = "int.mod"; + +// Remainder of division by zero is not defined. + +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: ERROR: Division by zero. +// CHECK:STDERR: let a: i32 = Mod(1, 0); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +let a: i32 = Mod(1, 0); + +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+3]]:14: ERROR: Division by zero. +// CHECK:STDERR: let b: i32 = Mod(0, 0); +// CHECK:STDERR: ^~~~ +let b: i32 = Mod(0, 0); + +// CHECK:STDOUT: --- int_div.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 5 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 2] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Mod = %Mod +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod: = fn_decl @Mod [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Mod.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Mod.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod.ref: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %.loc4_20: i32 = int_literal 5 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_23: i32 = int_literal 3 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_19: init i32 = call %Mod.ref(%.loc4_20, %.loc4_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 2] = var arr +// CHECK:STDOUT: %arr: ref [i32; 2] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 2 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.4] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 2] [template = constants.%.5] +// CHECK:STDOUT: %arr.ref: ref [i32; 2] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 2]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 2]* = bind_name arr_p, %.loc5_24 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mod(%a: i32, %b: i32) -> i32 = "int.mod"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Mod.ref: = name_ref Mod, file.%Mod [template = file.%Mod] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc8_13.1: init i32 = call %Mod.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc8_19: i32 = value_of_initializer %.loc8_13.1 +// CHECK:STDOUT: %.loc8_13.2: i32 = converted %.loc8_13.1, %.loc8_19 +// CHECK:STDOUT: return %.loc8_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 2147483647 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2147483649 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 4294967295 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.6: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Mod = %Mod +// CHECK:STDOUT: .Sub = %Sub +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod: = fn_decl @Mod [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Mod.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Mod.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var.loc4: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub: = fn_decl @Sub [template] { +// CHECK:STDOUT: %a.loc5_8.1: i32 = param a +// CHECK:STDOUT: @Sub.%a: i32 = bind_name a, %a.loc5_8.1 +// CHECK:STDOUT: %b.loc5_16.1: i32 = param b +// CHECK:STDOUT: @Sub.%b: i32 = bind_name b, %b.loc5_16.1 +// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %a.loc6_11.1: i32 = param a +// CHECK:STDOUT: @Negate.%a: i32 = bind_name a, %a.loc6_11.1 +// CHECK:STDOUT: %return.var.loc6: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod.ref.loc9: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %Negate.ref.loc9_18: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc9_25: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_24.1: init i32 = call %Negate.ref.loc9_18(%.loc9_25) [template = constants.%.2] +// CHECK:STDOUT: %Negate.ref.loc9_39: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc9_46: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc9_45.1: init i32 = call %Negate.ref.loc9_39(%.loc9_46) [template = constants.%.4] +// CHECK:STDOUT: %.loc9_17.1: i32 = value_of_initializer %.loc9_24.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc9_24.2: i32 = converted %.loc9_24.1, %.loc9_17.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc9_17.2: i32 = value_of_initializer %.loc9_45.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_45.2: i32 = converted %.loc9_45.1, %.loc9_17.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_17.3: init i32 = call %Mod.ref.loc9(%.loc9_24.2, %.loc9_45.2) [template = constants.%.5] +// CHECK:STDOUT: %.loc9_49: i32 = value_of_initializer %.loc9_17.3 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_17.4: i32 = converted %.loc9_17.3, %.loc9_49 [template = constants.%.5] +// CHECK:STDOUT: %a.loc9: i32 = bind_name a, %.loc9_17.4 +// CHECK:STDOUT: %Mod.ref.loc12: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %Sub.ref.loc12: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Negate.ref.loc12: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc12_29: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_28.1: init i32 = call %Negate.ref.loc12(%.loc12_29) [template = constants.%.2] +// CHECK:STDOUT: %.loc12_43: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc12_21.1: i32 = value_of_initializer %.loc12_28.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_28.2: i32 = converted %.loc12_28.1, %.loc12_21.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_21.2: init i32 = call %Sub.ref.loc12(%.loc12_28.2, %.loc12_43) [template = constants.%.6] +// CHECK:STDOUT: %.loc12_47: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc12_17.1: i32 = value_of_initializer %.loc12_21.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc12_21.3: i32 = converted %.loc12_21.2, %.loc12_17.1 [template = constants.%.6] +// CHECK:STDOUT: %.loc12_17.2: init i32 = call %Mod.ref.loc12(%.loc12_21.3, %.loc12_47) [template = constants.%.5] +// CHECK:STDOUT: %.loc12_49: i32 = value_of_initializer %.loc12_17.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_17.3: i32 = converted %.loc12_17.2, %.loc12_49 [template = constants.%.5] +// CHECK:STDOUT: %b.loc12: i32 = bind_name b, %.loc12_17.3 +// CHECK:STDOUT: %Mod.ref.loc20: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %Sub.ref.loc20: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Negate.ref.loc20_22: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc20_29: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc20_28.1: init i32 = call %Negate.ref.loc20_22(%.loc20_29) [template = constants.%.2] +// CHECK:STDOUT: %.loc20_43: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc20_21.1: i32 = value_of_initializer %.loc20_28.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc20_28.2: i32 = converted %.loc20_28.1, %.loc20_21.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc20_21.2: init i32 = call %Sub.ref.loc20(%.loc20_28.2, %.loc20_43) [template = constants.%.6] +// CHECK:STDOUT: %Negate.ref.loc20_47: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc20_54: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc20_53.1: init i32 = call %Negate.ref.loc20_47(%.loc20_54) [template = constants.%.4] +// CHECK:STDOUT: %.loc20_17.1: i32 = value_of_initializer %.loc20_21.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc20_21.3: i32 = converted %.loc20_21.2, %.loc20_17.1 [template = constants.%.6] +// CHECK:STDOUT: %.loc20_17.2: i32 = value_of_initializer %.loc20_53.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc20_53.2: i32 = converted %.loc20_53.1, %.loc20_17.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc20_17.3: init i32 = call %Mod.ref.loc20(%.loc20_21.3, %.loc20_53.2) [template = constants.%.5] +// CHECK:STDOUT: %.loc20_57: i32 = value_of_initializer %.loc20_17.3 [template = constants.%.5] +// CHECK:STDOUT: %.loc20_17.4: i32 = converted %.loc20_17.3, %.loc20_57 [template = constants.%.5] +// CHECK:STDOUT: %c: i32 = bind_name c, %.loc20_17.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mod(%a: i32, %b: i32) -> i32 = "int.mod"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Sub(%a: i32, %b: i32) -> i32 = "int.sub"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%a: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_div_by_zero.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Mod = %Mod +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod: = fn_decl @Mod [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Mod.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Mod.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mod.ref.loc12: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %.loc12_18: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_21: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_17.1: init i32 = call %Mod.ref.loc12(%.loc12_18, %.loc12_21) [template = ] +// CHECK:STDOUT: %.loc12_23: i32 = value_of_initializer %.loc12_17.1 [template = ] +// CHECK:STDOUT: %.loc12_17.2: i32 = converted %.loc12_17.1, %.loc12_23 [template = ] +// CHECK:STDOUT: %a.loc12: i32 = bind_name a, %.loc12_17.2 +// CHECK:STDOUT: %Mod.ref.loc17: = name_ref Mod, %Mod [template = %Mod] +// CHECK:STDOUT: %.loc17_18: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc17_21: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc17_17.1: init i32 = call %Mod.ref.loc17(%.loc17_18, %.loc17_21) [template = ] +// CHECK:STDOUT: %.loc17_23: i32 = value_of_initializer %.loc17_17.1 [template = ] +// CHECK:STDOUT: %.loc17_17.2: i32 = converted %.loc17_17.1, %.loc17_23 [template = ] +// CHECK:STDOUT: %b.loc17: i32 = bind_name b, %.loc17_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mod(%a: i32, %b: i32) -> i32 = "int.mod"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_mul.carbon b/toolchain/check/testdata/builtins/int_mul.carbon new file mode 100644 index 000000000000..bf41a7b81f3a --- /dev/null +++ b/toolchain/check/testdata/builtins/int_mul.carbon @@ -0,0 +1,126 @@ +// 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 + +// --- int_mul.carbon + +fn Mul(a: i32, b: i32) -> i32 = "int.mul"; + +var arr: [i32; Mul(3, 2)]; +let arr_p: [i32; 6]* = &arr; + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Mul(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Mul(a: i32, b: i32) -> i32 = "int.mul"; + +let a: i32 = Mul(0x7FFF, 0x10000); +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: ERROR: Integer overflow in calculation 32768 * 65536. +// CHECK:STDERR: let b: i32 = Mul(0x8000, 0x10000); +// CHECK:STDERR: ^~~~ +let b: i32 = Mul(0x8000, 0x10000); + +// CHECK:STDOUT: --- int_mul.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 6 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 6] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Mul = %Mul +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mul: = fn_decl @Mul [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Mul.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Mul.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mul.ref: = name_ref Mul, %Mul [template = %Mul] +// CHECK:STDOUT: %.loc4_20: i32 = int_literal 3 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_23: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_19: init i32 = call %Mul.ref(%.loc4_20, %.loc4_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 6] = var arr +// CHECK:STDOUT: %arr: ref [i32; 6] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 6 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.4] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 6] [template = constants.%.5] +// CHECK:STDOUT: %arr.ref: ref [i32; 6] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 6]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 6]* = bind_name arr_p, %.loc5_24 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mul(%a: i32, %b: i32) -> i32 = "int.mul"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Mul.ref: = name_ref Mul, file.%Mul [template = file.%Mul] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc8_13.1: init i32 = call %Mul.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc8_19: i32 = value_of_initializer %.loc8_13.1 +// CHECK:STDOUT: %.loc8_13.2: i32 = converted %.loc8_13.1, %.loc8_19 +// CHECK:STDOUT: return %.loc8_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 32767 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 65536 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 2147418112 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 32768 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Mul = %Mul +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mul: = fn_decl @Mul [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Mul.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Mul.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mul.ref.loc6: = name_ref Mul, %Mul [template = %Mul] +// CHECK:STDOUT: %.loc6_18: i32 = int_literal 32767 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_26: i32 = int_literal 65536 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_17.1: init i32 = call %Mul.ref.loc6(%.loc6_18, %.loc6_26) [template = constants.%.3] +// CHECK:STDOUT: %.loc6_34: i32 = value_of_initializer %.loc6_17.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc6_17.2: i32 = converted %.loc6_17.1, %.loc6_34 [template = constants.%.3] +// CHECK:STDOUT: %a.loc6: i32 = bind_name a, %.loc6_17.2 +// CHECK:STDOUT: %Mul.ref.loc10: = name_ref Mul, %Mul [template = %Mul] +// CHECK:STDOUT: %.loc10_18: i32 = int_literal 32768 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_26: i32 = int_literal 65536 [template = constants.%.2] +// CHECK:STDOUT: %.loc10_17.1: init i32 = call %Mul.ref.loc10(%.loc10_18, %.loc10_26) [template = constants.%.5] +// CHECK:STDOUT: %.loc10_34: i32 = value_of_initializer %.loc10_17.1 [template = constants.%.5] +// CHECK:STDOUT: %.loc10_17.2: i32 = converted %.loc10_17.1, %.loc10_34 [template = constants.%.5] +// CHECK:STDOUT: %b.loc10: i32 = bind_name b, %.loc10_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mul(%a: i32, %b: i32) -> i32 = "int.mul"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_negate.carbon b/toolchain/check/testdata/builtins/int_negate.carbon new file mode 100644 index 000000000000..cae22934e2fa --- /dev/null +++ b/toolchain/check/testdata/builtins/int_negate.carbon @@ -0,0 +1,361 @@ +// 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 + +// --- int_negate.carbon + +fn Negate(a: i32) -> i32 = "int.negate"; + +var arr: [i32; Negate(Negate(1))]; +let arr_p: [i32; 1]* = &arr; + +let n: i32 = Negate(1); + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Negate(a); +} + +// --- fail_bad_decl.carbon + +package FailBadDecl api; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: ERROR: Invalid signature for builtin function "int.negate". +// CHECK:STDERR: fn TooFew() -> i32 = "int.negate"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn TooFew() -> i32 = "int.negate"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: ERROR: Invalid signature for builtin function "int.negate". +// CHECK:STDERR: fn TooMany(a: i32, b: i32) -> i32 = "int.negate"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn TooMany(a: i32, b: i32) -> i32 = "int.negate"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: ERROR: Invalid signature for builtin function "int.negate". +// CHECK:STDERR: fn BadReturnType(a: i32) -> bool = "int.negate"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn BadReturnType(a: i32) -> bool = "int.negate"; +fn JustRight(a: i32) -> i32 = "int.negate"; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:20: ERROR: Array bound is not a constant. +// CHECK:STDERR: var too_few: [i32; TooFew()]; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +var too_few: [i32; TooFew()]; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:21: ERROR: Array bound is not a constant. +// CHECK:STDERR: var too_many: [i32; TooMany(1, 2)]; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +var too_many: [i32; TooMany(1, 2)]; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:28: ERROR: Array bound is not a constant. +// CHECK:STDERR: var bad_return_type: [i32; BadReturnType(1)]; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +var bad_return_type: [i32; BadReturnType(1)]; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+7]]:21: ERROR: 2 argument(s) passed to function expecting 1 argument(s). +// CHECK:STDERR: var bad_call: [i32; JustRight(1, 2)]; +// CHECK:STDERR: ^~~~~~~~~~ +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-21]]:1: Calling function declared here. +// CHECK:STDERR: fn JustRight(a: i32) -> i32 = "int.negate"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var bad_call: [i32; JustRight(1, 2)]; + +fn RuntimeCallTooFew(a: i32) -> i32 { + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+7]]:10: ERROR: 1 argument(s) passed to function expecting 0 argument(s). + // CHECK:STDERR: return TooFew(a); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-42]]:1: Calling function declared here. + // CHECK:STDERR: fn TooFew() -> i32 = "int.negate"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return TooFew(a); +} + +fn RuntimeCallTooMany(a: i32, b: i32, c: i32) -> i32 { + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+7]]:10: ERROR: 3 argument(s) passed to function expecting 2 argument(s). + // CHECK:STDERR: return TooMany(a, b, c); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-48]]:1: Calling function declared here. + // CHECK:STDERR: fn TooMany(a: i32, b: i32) -> i32 = "int.negate"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return TooMany(a, b, c); +} + +fn RuntimeCallBadReturnType(a: i32, b: i32) -> bool { + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+7]]:10: ERROR: 2 argument(s) passed to function expecting 1 argument(s). + // CHECK:STDERR: return BadReturnType(a, b); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-54]]:1: Calling function declared here. + // CHECK:STDERR: fn BadReturnType(a: i32) -> bool = "int.negate"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return BadReturnType(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Negate(a: i32) -> i32 = "int.negate"; +fn Sub(a: i32, b: i32) -> i32 = "int.sub"; + +// -(-INT_MAX) is INT_MAX. +let a: i32 = Negate(Negate(0x7FFFFFFF)); + +// -(-INT_MAX - 1) is too large for i32. +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: ERROR: Integer overflow in negation of -2147483648. +// CHECK:STDERR: let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1)); +// CHECK:STDERR: ^~~~~~~ +let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1)); + +// CHECK:STDOUT: --- int_negate.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 4294967295 [template] +// CHECK:STDOUT: %.3: type = array_type %.1, i32 [template] +// CHECK:STDOUT: %.4: type = ptr_type [i32; 1] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %a.loc2_11.1: i32 = param a +// CHECK:STDOUT: @Negate.%a: i32 = bind_name a, %a.loc2_11.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate.ref.loc4_16: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %Negate.ref.loc4_23: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc4_30: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_29.1: init i32 = call %Negate.ref.loc4_23(%.loc4_30) [template = constants.%.2] +// CHECK:STDOUT: %.loc4_22.1: i32 = value_of_initializer %.loc4_29.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_29.2: i32 = converted %.loc4_29.1, %.loc4_22.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_22.2: init i32 = call %Negate.ref.loc4_16(%.loc4_29.2) [template = constants.%.1] +// CHECK:STDOUT: %.loc4_33: type = array_type %.loc4_22.2, i32 [template = constants.%.3] +// CHECK:STDOUT: %arr.var: ref [i32; 1] = var arr +// CHECK:STDOUT: %arr: ref [i32; 1] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 1] [template = constants.%.4] +// CHECK:STDOUT: %arr.ref: ref [i32; 1] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 1]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 1]* = bind_name arr_p, %.loc5_24 +// CHECK:STDOUT: %Negate.ref.loc7: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc7_21: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc7_20.1: init i32 = call %Negate.ref.loc7(%.loc7_21) [template = constants.%.2] +// CHECK:STDOUT: %.loc7_23: i32 = value_of_initializer %.loc7_20.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc7_20.2: i32 = converted %.loc7_20.1, %.loc7_23 [template = constants.%.2] +// CHECK:STDOUT: %n: i32 = bind_name n, %.loc7_20.2 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc9_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc9_16.1 +// CHECK:STDOUT: %b.loc9_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc9_24.1 +// CHECK:STDOUT: %return.var.loc9: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%a: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Negate.ref: = name_ref Negate, file.%Negate [template = file.%Negate] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %.loc10_16.1: init i32 = call %Negate.ref(%a.ref) +// CHECK:STDOUT: %.loc10_19: i32 = value_of_initializer %.loc10_16.1 +// CHECK:STDOUT: %.loc10_16.2: i32 = converted %.loc10_16.1, %.loc10_19 +// CHECK:STDOUT: return %.loc10_16.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_bad_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .TooFew = %TooFew +// CHECK:STDOUT: .TooMany = %TooMany +// CHECK:STDOUT: .BadReturnType = %BadReturnType +// CHECK:STDOUT: .JustRight = %JustRight +// CHECK:STDOUT: .too_few = %too_few +// CHECK:STDOUT: .too_many = %too_many +// CHECK:STDOUT: .bad_return_type = %bad_return_type +// CHECK:STDOUT: .bad_call = %bad_call +// CHECK:STDOUT: .RuntimeCallTooFew = %RuntimeCallTooFew +// CHECK:STDOUT: .RuntimeCallTooMany = %RuntimeCallTooMany +// CHECK:STDOUT: .RuntimeCallBadReturnType = %RuntimeCallBadReturnType +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooFew: = fn_decl @TooFew [template] { +// CHECK:STDOUT: %return.var.loc8: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooMany: = fn_decl @TooMany [template] { +// CHECK:STDOUT: %a.loc13_12.1: i32 = param a +// CHECK:STDOUT: @TooMany.%a: i32 = bind_name a, %a.loc13_12.1 +// CHECK:STDOUT: %b.loc13_20.1: i32 = param b +// CHECK:STDOUT: @TooMany.%b: i32 = bind_name b, %b.loc13_20.1 +// CHECK:STDOUT: %return.var.loc13: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %BadReturnType: = fn_decl @BadReturnType [template] { +// CHECK:STDOUT: %a.loc18_18.1: i32 = param a +// CHECK:STDOUT: @BadReturnType.%a: i32 = bind_name a, %a.loc18_18.1 +// CHECK:STDOUT: %return.var.loc18: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %JustRight: = fn_decl @JustRight [template] { +// CHECK:STDOUT: %a.loc19_14.1: i32 = param a +// CHECK:STDOUT: @JustRight.%a: i32 = bind_name a, %a.loc19_14.1 +// CHECK:STDOUT: %return.var.loc19: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooFew.ref: = name_ref TooFew, %TooFew [template = %TooFew] +// CHECK:STDOUT: %.loc25: init i32 = call %TooFew.ref() +// CHECK:STDOUT: %too_few.var: ref = var too_few +// CHECK:STDOUT: %too_few: ref = bind_name too_few, %too_few.var +// CHECK:STDOUT: %TooMany.ref: = name_ref TooMany, %TooMany [template = %TooMany] +// CHECK:STDOUT: %.loc30_29: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc30_32: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc30_28: init i32 = call %TooMany.ref(%.loc30_29, %.loc30_32) +// CHECK:STDOUT: %too_many.var: ref = var too_many +// CHECK:STDOUT: %too_many: ref = bind_name too_many, %too_many.var +// CHECK:STDOUT: %BadReturnType.ref: = name_ref BadReturnType, %BadReturnType [template = %BadReturnType] +// CHECK:STDOUT: %.loc35_42: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc35_41: init bool = call %BadReturnType.ref(%.loc35_42) +// CHECK:STDOUT: %bad_return_type.var: ref = var bad_return_type +// CHECK:STDOUT: %bad_return_type: ref = bind_name bad_return_type, %bad_return_type.var +// CHECK:STDOUT: %JustRight.ref: = name_ref JustRight, %JustRight [template = %JustRight] +// CHECK:STDOUT: %.loc44_31: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc44_34: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc44_30: init i32 = call %JustRight.ref() [template = ] +// CHECK:STDOUT: %.loc44_36: type = array_type %.loc44_30, i32 [template = ] +// CHECK:STDOUT: %bad_call.var: ref = var bad_call +// CHECK:STDOUT: %bad_call: ref = bind_name bad_call, %bad_call.var +// CHECK:STDOUT: %RuntimeCallTooFew: = fn_decl @RuntimeCallTooFew [template] { +// CHECK:STDOUT: %a.loc46_22.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallTooFew.%a: i32 = bind_name a, %a.loc46_22.1 +// CHECK:STDOUT: %return.var.loc46: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %RuntimeCallTooMany: = fn_decl @RuntimeCallTooMany [template] { +// CHECK:STDOUT: %a.loc57_23.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallTooMany.%a: i32 = bind_name a, %a.loc57_23.1 +// CHECK:STDOUT: %b.loc57_31.1: i32 = param b +// CHECK:STDOUT: @RuntimeCallTooMany.%b: i32 = bind_name b, %b.loc57_31.1 +// CHECK:STDOUT: %c.loc57_39.1: i32 = param c +// CHECK:STDOUT: @RuntimeCallTooMany.%c: i32 = bind_name c, %c.loc57_39.1 +// CHECK:STDOUT: %return.var.loc57: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %RuntimeCallBadReturnType: = fn_decl @RuntimeCallBadReturnType [template] { +// CHECK:STDOUT: %a.loc68_29.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallBadReturnType.%a: i32 = bind_name a, %a.loc68_29.1 +// CHECK:STDOUT: %b.loc68_37.1: i32 = param b +// CHECK:STDOUT: @RuntimeCallBadReturnType.%b: i32 = bind_name b, %b.loc68_37.1 +// CHECK:STDOUT: %return.var.loc68: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TooFew() -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TooMany(%a: i32, %b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @BadReturnType(%a: i32) -> bool; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @JustRight(%a: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallTooFew(%a: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %TooFew.ref: = name_ref TooFew, file.%TooFew [template = file.%TooFew] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %.loc54_16.1: init i32 = call %TooFew.ref() [template = ] +// CHECK:STDOUT: %.loc54_19: i32 = value_of_initializer %.loc54_16.1 [template = ] +// CHECK:STDOUT: %.loc54_16.2: i32 = converted %.loc54_16.1, %.loc54_19 [template = ] +// CHECK:STDOUT: return %.loc54_16.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallTooMany(%a: i32, %b: i32, %c: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %TooMany.ref: = name_ref TooMany, file.%TooMany [template = file.%TooMany] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %c.ref: i32 = name_ref c, %c +// CHECK:STDOUT: %.loc65_17.1: init i32 = call %TooMany.ref() [template = ] +// CHECK:STDOUT: %.loc65_26: i32 = value_of_initializer %.loc65_17.1 [template = ] +// CHECK:STDOUT: %.loc65_17.2: i32 = converted %.loc65_17.1, %.loc65_26 [template = ] +// CHECK:STDOUT: return %.loc65_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallBadReturnType(%a: i32, %b: i32) -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %BadReturnType.ref: = name_ref BadReturnType, file.%BadReturnType [template = file.%BadReturnType] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc76_23.1: init bool = call %BadReturnType.ref() [template = ] +// CHECK:STDOUT: %.loc76_29: bool = value_of_initializer %.loc76_23.1 [template = ] +// CHECK:STDOUT: %.loc76_23.2: bool = converted %.loc76_23.1, %.loc76_29 [template = ] +// CHECK:STDOUT: return %.loc76_23.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 2147483647 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2147483649 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: .Sub = %Sub +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %a.loc4_11.1: i32 = param a +// CHECK:STDOUT: @Negate.%a: i32 = bind_name a, %a.loc4_11.1 +// CHECK:STDOUT: %return.var.loc4: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub: = fn_decl @Sub [template] { +// CHECK:STDOUT: %a.loc5_8.1: i32 = param a +// CHECK:STDOUT: @Sub.%a: i32 = bind_name a, %a.loc5_8.1 +// CHECK:STDOUT: %b.loc5_16.1: i32 = param b +// CHECK:STDOUT: @Sub.%b: i32 = bind_name b, %b.loc5_16.1 +// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Negate.ref.loc8_14: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %Negate.ref.loc8_21: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc8_28: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc8_27.1: init i32 = call %Negate.ref.loc8_21(%.loc8_28) [template = constants.%.2] +// CHECK:STDOUT: %.loc8_20.1: i32 = value_of_initializer %.loc8_27.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc8_27.2: i32 = converted %.loc8_27.1, %.loc8_20.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc8_20.2: init i32 = call %Negate.ref.loc8_14(%.loc8_27.2) [template = constants.%.1] +// CHECK:STDOUT: %.loc8_40: i32 = value_of_initializer %.loc8_20.2 [template = constants.%.1] +// CHECK:STDOUT: %.loc8_20.3: i32 = converted %.loc8_20.2, %.loc8_40 [template = constants.%.1] +// CHECK:STDOUT: %a.loc8: i32 = bind_name a, %.loc8_20.3 +// CHECK:STDOUT: %Negate.ref.loc14_14: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %Sub.ref: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Negate.ref.loc14_25: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc14_32: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc14_31.1: init i32 = call %Negate.ref.loc14_25(%.loc14_32) [template = constants.%.2] +// CHECK:STDOUT: %.loc14_45: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc14_24.1: i32 = value_of_initializer %.loc14_31.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc14_31.2: i32 = converted %.loc14_31.1, %.loc14_24.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc14_24.2: init i32 = call %Sub.ref(%.loc14_31.2, %.loc14_45) [template = constants.%.4] +// CHECK:STDOUT: %.loc14_20.1: i32 = value_of_initializer %.loc14_24.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc14_24.3: i32 = converted %.loc14_24.2, %.loc14_20.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc14_20.2: init i32 = call %Negate.ref.loc14_14(%.loc14_24.3) [template = constants.%.4] +// CHECK:STDOUT: %.loc14_48: i32 = value_of_initializer %.loc14_20.2 [template = constants.%.4] +// CHECK:STDOUT: %.loc14_20.3: i32 = converted %.loc14_20.2, %.loc14_48 [template = constants.%.4] +// CHECK:STDOUT: %b.loc14: i32 = bind_name b, %.loc14_20.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%a: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Sub(%a: i32, %b: i32) -> i32 = "int.sub"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_neq.carbon b/toolchain/check/testdata/builtins/int_neq.carbon new file mode 100644 index 000000000000..c49e74b55b3b --- /dev/null +++ b/toolchain/check/testdata/builtins/int_neq.carbon @@ -0,0 +1,75 @@ +// 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 + +// --- int_neq.carbon + +fn Neq(a: i32, b: i32) -> bool = "int.neq"; + +// TODO: Use a test that will fail to compile if we get the wrong value. +let b_true: bool = Neq(1, 2); +let b_false: bool = Neq(1, 1); + +fn RuntimeCall(a: i32, b: i32) -> bool { + return Neq(a, b); +} + +// CHECK:STDOUT: --- int_neq.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: bool = bool_literal true [template] +// CHECK:STDOUT: %.4: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Neq = %Neq +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Neq: = fn_decl @Neq [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Neq.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Neq.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Neq.ref.loc5: = name_ref Neq, %Neq [template = %Neq] +// CHECK:STDOUT: %.loc5_24: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_27: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc5_23.1: init bool = call %Neq.ref.loc5(%.loc5_24, %.loc5_27) [template = constants.%.3] +// CHECK:STDOUT: %.loc5_29: bool = value_of_initializer %.loc5_23.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_23.2: bool = converted %.loc5_23.1, %.loc5_29 [template = constants.%.3] +// CHECK:STDOUT: %b_true: bool = bind_name b_true, %.loc5_23.2 +// CHECK:STDOUT: %Neq.ref.loc6: = name_ref Neq, %Neq [template = %Neq] +// CHECK:STDOUT: %.loc6_25: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_28: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_24.1: init bool = call %Neq.ref.loc6(%.loc6_25, %.loc6_28) [template = constants.%.4] +// CHECK:STDOUT: %.loc6_30: bool = value_of_initializer %.loc6_24.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc6_24.2: bool = converted %.loc6_24.1, %.loc6_30 [template = constants.%.4] +// CHECK:STDOUT: %b_false: bool = bind_name b_false, %.loc6_24.2 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc8_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc8_16.1 +// CHECK:STDOUT: %b.loc8_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc8_24.1 +// CHECK:STDOUT: %return.var.loc8: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Neq(%a: i32, %b: i32) -> bool = "int.neq"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Neq.ref: = name_ref Neq, file.%Neq [template = file.%Neq] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc9_13.1: init bool = call %Neq.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc9_19: bool = value_of_initializer %.loc9_13.1 +// CHECK:STDOUT: %.loc9_13.2: bool = converted %.loc9_13.1, %.loc9_19 +// CHECK:STDOUT: return %.loc9_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int_sub.carbon b/toolchain/check/testdata/builtins/int_sub.carbon new file mode 100644 index 000000000000..c64a8b0030f0 --- /dev/null +++ b/toolchain/check/testdata/builtins/int_sub.carbon @@ -0,0 +1,145 @@ +// 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 + +// --- int_sub.carbon + +fn Sub(a: i32, b: i32) -> i32 = "int.sub"; + +var arr: [i32; Sub(3, 2)]; +let arr_p: [i32; 1]* = &arr; + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Sub(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Sub(a: i32, b: i32) -> i32 = "int.sub"; + +let a: i32 = Sub(0, 0x7FFFFFFF); +let b: i32 = Sub(Sub(0, 0x7FFFFFFF), 1); +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: ERROR: Integer overflow in calculation -2147483647 - 2. +// CHECK:STDERR: let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); +// CHECK:STDERR: ^~~~ +let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); + +// CHECK:STDOUT: --- int_sub.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 1] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Sub = %Sub +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub: = fn_decl @Sub [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Sub.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Sub.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub.ref: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %.loc4_20: i32 = int_literal 3 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_23: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_19: init i32 = call %Sub.ref(%.loc4_20, %.loc4_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 1] = var arr +// CHECK:STDOUT: %arr: ref [i32; 1] = bind_name arr, %arr.var +// CHECK:STDOUT: %.loc5_18: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc5_19: type = array_type %.loc5_18, i32 [template = constants.%.4] +// CHECK:STDOUT: %.loc5_20: type = ptr_type [i32; 1] [template = constants.%.5] +// CHECK:STDOUT: %arr.ref: ref [i32; 1] = name_ref arr, %arr +// CHECK:STDOUT: %.loc5_24: [i32; 1]* = addr_of %arr.ref +// CHECK:STDOUT: %arr_p: [i32; 1]* = bind_name arr_p, %.loc5_24 +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc7_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc7_16.1 +// CHECK:STDOUT: %b.loc7_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc7_24.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Sub(%a: i32, %b: i32) -> i32 = "int.sub"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Sub.ref: = name_ref Sub, file.%Sub [template = file.%Sub] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc8_13.1: init i32 = call %Sub.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc8_19: i32 = value_of_initializer %.loc8_13.1 +// CHECK:STDOUT: %.loc8_13.2: i32 = converted %.loc8_13.1, %.loc8_19 +// CHECK:STDOUT: return %.loc8_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2147483647 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 2147483649 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: %.6: i32 = int_literal 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Sub = %Sub +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub: = fn_decl @Sub [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Sub.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Sub.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Sub.ref.loc6: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %.loc6_18: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_21: i32 = int_literal 2147483647 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_17.1: init i32 = call %Sub.ref.loc6(%.loc6_18, %.loc6_21) [template = constants.%.3] +// CHECK:STDOUT: %.loc6_32: i32 = value_of_initializer %.loc6_17.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc6_17.2: i32 = converted %.loc6_17.1, %.loc6_32 [template = constants.%.3] +// CHECK:STDOUT: %a.loc6: i32 = bind_name a, %.loc6_17.2 +// CHECK:STDOUT: %Sub.ref.loc7_14: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Sub.ref.loc7_18: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %.loc7_22: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %.loc7_25: i32 = int_literal 2147483647 [template = constants.%.2] +// CHECK:STDOUT: %.loc7_21.1: init i32 = call %Sub.ref.loc7_18(%.loc7_22, %.loc7_25) [template = constants.%.3] +// CHECK:STDOUT: %.loc7_38: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc7_17.1: i32 = value_of_initializer %.loc7_21.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc7_21.2: i32 = converted %.loc7_21.1, %.loc7_17.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc7_17.2: init i32 = call %Sub.ref.loc7_14(%.loc7_21.2, %.loc7_38) [template = constants.%.5] +// CHECK:STDOUT: %.loc7_40: i32 = value_of_initializer %.loc7_17.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc7_17.3: i32 = converted %.loc7_17.2, %.loc7_40 [template = constants.%.5] +// CHECK:STDOUT: %b.loc7: i32 = bind_name b, %.loc7_17.3 +// CHECK:STDOUT: %Sub.ref.loc11_14: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %Sub.ref.loc11_18: = name_ref Sub, %Sub [template = %Sub] +// CHECK:STDOUT: %.loc11_22: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %.loc11_25: i32 = int_literal 2147483647 [template = constants.%.2] +// CHECK:STDOUT: %.loc11_21.1: init i32 = call %Sub.ref.loc11_18(%.loc11_22, %.loc11_25) [template = constants.%.3] +// CHECK:STDOUT: %.loc11_38: i32 = int_literal 2 [template = constants.%.6] +// CHECK:STDOUT: %.loc11_17.1: i32 = value_of_initializer %.loc11_21.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc11_21.2: i32 = converted %.loc11_21.1, %.loc11_17.1 [template = constants.%.3] +// CHECK:STDOUT: %.loc11_17.2: init i32 = call %Sub.ref.loc11_14(%.loc11_21.2, %.loc11_38) [template = constants.%.2] +// CHECK:STDOUT: %.loc11_40: i32 = value_of_initializer %.loc11_17.2 [template = constants.%.2] +// CHECK:STDOUT: %.loc11_17.3: i32 = converted %.loc11_17.2, %.loc11_40 [template = constants.%.2] +// CHECK:STDOUT: %c: i32 = bind_name c, %.loc11_17.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Sub(%a: i32, %b: i32) -> i32 = "int.sub"; +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 546f36c0d30d..00823cb05966 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -229,13 +229,16 @@ CARBON_DIAGNOSTIC_KIND(AddrOfEphemeralRef) CARBON_DIAGNOSTIC_KIND(AddrOfNonRef) CARBON_DIAGNOSTIC_KIND(AddrOnNonSelfParam) CARBON_DIAGNOSTIC_KIND(ArrayBoundTooLarge) +CARBON_DIAGNOSTIC_KIND(ArrayBoundNegative) CARBON_DIAGNOSTIC_KIND(ArrayIndexOutOfBounds) CARBON_DIAGNOSTIC_KIND(ArrayInitFromLiteralArgCountMismatch) CARBON_DIAGNOSTIC_KIND(ArrayInitFromExprArgCountMismatch) CARBON_DIAGNOSTIC_KIND(ArrowOperatorOfNonPointer) CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable) CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop) +CARBON_DIAGNOSTIC_KIND(CompileTimeDivisionByZero) CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerOverflow) +CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerNegateOverflow) CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop) CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType) CARBON_DIAGNOSTIC_KIND(DerefOfNonPointer) diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 553705c6458a..69c69a786f23 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -172,12 +172,24 @@ auto HandleBuiltin(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, SemIR::BuiltinFunctionKind builtin_kind, llvm::ArrayRef arg_ids) -> void { + constexpr bool SignedOverflowIsUB = false; + switch (builtin_kind) { case SemIR::BuiltinFunctionKind::None: CARBON_FATAL() << "No callee in function call."; + case SemIR::BuiltinFunctionKind::IntNegate: { + // Lower `-x` as `0 - x`. + auto* operand = context.GetValue(arg_ids[0]); + context.SetLocal(inst_id, + context.builder().CreateSub( + llvm::ConstantInt::getNullValue(operand->getType()), + operand, "neg", + /*HasNUW=*/false, + /*HasNSW=*/SignedOverflowIsUB)); + return; + } case SemIR::BuiltinFunctionKind::IntAdd: { - constexpr bool SignedOverflowIsUB = false; context.SetLocal(inst_id, context.builder().CreateAdd( context.GetValue(arg_ids[0]), context.GetValue(arg_ids[1]), "add", @@ -185,6 +197,46 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, /*HasNSW=*/SignedOverflowIsUB)); return; } + case SemIR::BuiltinFunctionKind::IntSub: { + context.SetLocal(inst_id, context.builder().CreateSub( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "sub", + /*HasNUW=*/false, + /*HasNSW=*/SignedOverflowIsUB)); + return; + } + case SemIR::BuiltinFunctionKind::IntMul: { + context.SetLocal(inst_id, context.builder().CreateMul( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "mul", + /*HasNUW=*/false, + /*HasNSW=*/SignedOverflowIsUB)); + return; + } + case SemIR::BuiltinFunctionKind::IntDiv: { + context.SetLocal(inst_id, context.builder().CreateSDiv( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "div")); + return; + } + case SemIR::BuiltinFunctionKind::IntMod: { + context.SetLocal(inst_id, context.builder().CreateSRem( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "rem")); + return; + } + case SemIR::BuiltinFunctionKind::IntEq: { + context.SetLocal(inst_id, context.builder().CreateICmpEQ( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "eq")); + return; + } + case SemIR::BuiltinFunctionKind::IntNeq: { + context.SetLocal(inst_id, context.builder().CreateICmpNE( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "neq")); + return; + } } CARBON_FATAL() << "Unsupported builtin call."; diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 78558f8d8977..268f7d2cf2da 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -50,19 +50,24 @@ struct TypeParam { } }; -// Constraint that requires the type to be an integer type. See -// ValidateSignature for details. -struct AnyInt { +// Constraint that a type is a specific builtin. See ValidateSignature for +// details. +template +struct BuiltinType { static auto Check(const File& sem_ir, ValidateState& /*state*/, TypeId type_id) -> bool { - if (sem_ir.types().GetInstId(type_id) == InstId::BuiltinIntType) { - return true; - } - // TODO: Support iN for all N, and the Core.BigInt type we use to implement - // for integer literals. - return false; + return sem_ir.types().GetInstId(type_id) == BuiltinId; } }; + +// Constraint that a type is `bool`. +using Bool = BuiltinType; + +// Constraint that requires the type to be an integer type. +// +// TODO: This only matches i32 for now. Support iN for all N, and the +// Core.BigInt type we use to implement for integer literals. +using AnyInt = BuiltinType; } // namespace // Validates that this builtin has a signature matching the specified signature. @@ -123,10 +128,38 @@ using IntT = TypeParam<0, AnyInt>; // Not a builtin function. constexpr BuiltinInfo None = {"", nullptr}; +// "int.negate": integer negation. +constexpr BuiltinInfo IntNegate = {"int.negate", + ValidateSignatureIntT>}; + // "int.add": integer addition. constexpr BuiltinInfo IntAdd = {"int.add", ValidateSignatureIntT>}; +// "int.sub": integer subtraction. +constexpr BuiltinInfo IntSub = {"int.sub", + ValidateSignatureIntT>}; + +// "int.mul": integer multiplication. +constexpr BuiltinInfo IntMul = {"int.mul", + ValidateSignatureIntT>}; + +// "int.div": integer division. +constexpr BuiltinInfo IntDiv = {"int.div", + ValidateSignatureIntT>}; + +// "int.mod": integer modulo. +constexpr BuiltinInfo IntMod = {"int.mod", + ValidateSignatureIntT>}; + +// "int.eq": integer equality comparison. +constexpr BuiltinInfo IntEq = {"int.eq", + ValidateSignatureBool>}; + +// "int.neq": integer non-equality comparison. +constexpr BuiltinInfo IntNeq = {"int.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 8fe4aab5d24b..600c49aba506 100644 --- a/toolchain/sem_ir/builtin_function_kind.def +++ b/toolchain/sem_ir/builtin_function_kind.def @@ -17,6 +17,13 @@ #endif CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(None) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntNegate) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntAdd) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntSub) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMul) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntDiv) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMod) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntEq) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntNeq) #undef CARBON_SEM_IR_BUILTIN_FUNCTION_KIND diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index d861caeea428..2fad9f47a6c7 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -265,6 +265,11 @@ struct BoolValue : public IdBase, public Printable { static const BoolValue False; static const BoolValue True; + // Returns the `BoolValue` corresponding to `b`. + static constexpr auto FromBool(bool b) -> BoolValue { + return b ? True : False; + } + using IdBase::IdBase; auto Print(llvm::raw_ostream& out) const -> void { if (*this == False) {