From 4a7aefefaa07bf5fe40f7ceb2990f792ea1e6f91 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 30 Dec 2024 22:36:43 -0800 Subject: [PATCH] Add support for operators on `Core.IntLiteral`. (#4716) Fixes integer builtins to produce the correct values (and not CHECK-fail) when used on integer literals. Also adds impls to the prelude to use the new builtins to perform operations on integer literals. Perhaps most importantly, this allows directly initializing `i32` values with negative numbers, as the negation operation on integer literals now works. For testing I've added tests for use of literals with one operator in each class (addition, multiplication, ordering, bitwise, etc) for which there are distinct rules or overflow behavior, rather than exhaustively testing all the combinations. This is aimed at finding a good tradeoff between maintainability of the tests and thorough test coverage. Also fixes lowering of heterogeneous shifts and comparisons. These are currently disabled when one of the operands is an integer literal, but we may want to allow that when the integer literal operand has a known constant value. --- core/io.carbon | 3 +- core/prelude/operators/arithmetic.carbon | 29 ++ core/prelude/operators/bitwise.carbon | 29 ++ core/prelude/operators/comparison.carbon | 17 + toolchain/base/int.h | 11 +- toolchain/check/eval.cpp | 342 +++++++++---- toolchain/check/testdata/array/base.carbon | 2 +- .../testdata/basics/builtin_types.carbon | 2 +- .../fail_numeric_literal_overflow.carbon | 2 +- .../testdata/basics/numeric_literals.carbon | 2 +- .../testdata/builtins/float/make_type.carbon | 4 +- .../check/testdata/builtins/int/and.carbon | 99 ++++ .../testdata/builtins/int/complement.carbon | 37 ++ .../check/testdata/builtins/int/eq.carbon | 71 ++- .../testdata/builtins/int/greater_eq.carbon | 38 ++ .../testdata/builtins/int/left_shift.carbon | 190 +++++++- .../testdata/builtins/int/less_eq.carbon | 72 +++ .../testdata/builtins/int/right_shift.carbon | 111 ++++- .../check/testdata/builtins/int/sadd.carbon | 62 ++- .../check/testdata/builtins/int/sdiv.carbon | 42 +- .../check/testdata/builtins/int/smod.carbon | 2 +- .../check/testdata/builtins/int/smul.carbon | 46 +- .../testdata/builtins/int/snegate.carbon | 25 +- .../check/testdata/builtins/int/ssub.carbon | 2 +- .../check/testdata/builtins/print/char.carbon | 6 +- .../check/testdata/builtins/print/int.carbon | 6 +- .../testdata/class/generic/import.carbon | 20 +- toolchain/check/testdata/class/import.carbon | 38 +- .../testdata/function/builtin/method.carbon | 2 +- .../testdata/if_expr/control_flow.carbon | 2 +- .../if_expr/fail_not_in_function.carbon | 2 +- .../index/fail_negative_indexing.carbon | 38 +- .../builtin/fail_type_mismatch_once.carbon | 13 +- .../builtin/fail_unimplemented_op.carbon | 10 +- .../overloaded/bit_complement.carbon | 4 +- .../testdata/operators/overloaded/dec.carbon | 4 +- .../testdata/operators/overloaded/eq.carbon | 2 +- .../overloaded/fail_error_recovery.carbon | 2 +- .../operators/overloaded/fail_no_impl.carbon | 6 +- .../overloaded/fail_no_impl_for_arg.carbon | 2 +- .../testdata/operators/overloaded/inc.carbon | 4 +- .../operators/overloaded/negate.carbon | 4 +- .../packages/implicit_imports_prelude.carbon | 2 +- .../check/testdata/pointer/import.carbon | 2 +- .../testdata/return/returned_var_scope.carbon | 2 +- toolchain/check/testdata/struct/import.carbon | 30 +- .../access/fail_negative_indexing.carbon | 17 +- .../tuple/access/index_not_literal.carbon | 2 +- toolchain/check/testdata/tuple/import.carbon | 30 +- toolchain/diagnostics/diagnostic_kind.def | 2 + toolchain/lower/constant.cpp | 11 +- toolchain/lower/file_context.h | 6 + toolchain/lower/function_context.h | 5 + toolchain/lower/handle_call.cpp | 101 +++- toolchain/lower/testdata/builtins/int.carbon | 460 +++++++++++++++--- .../testdata/builtins/int_literal.carbon | 33 ++ .../lower/testdata/builtins/print_read.carbon | 2 +- toolchain/sem_ir/builtin_function_kind.cpp | 144 ++++-- toolchain/sem_ir/builtin_function_kind.h | 3 +- 59 files changed, 1838 insertions(+), 419 deletions(-) diff --git a/core/io.carbon b/core/io.carbon index 80c1aa3acf051..b5a917e37a45f 100644 --- a/core/io.carbon +++ b/core/io.carbon @@ -15,5 +15,4 @@ fn PrintChar(x: i32) -> i32 = "print.char"; fn ReadChar() -> i32 = "read.char"; // TODO: Change this to a global constant once they are fully supported. -// TODO: Use simply -1 once we support negate on an IntLiteral. -fn EOF() -> i32 { return -(1 as i32); } +fn EOF() -> i32 { return -1; } diff --git a/core/prelude/operators/arithmetic.carbon b/core/prelude/operators/arithmetic.carbon index 693d0571f2b42..36705e2cc5678 100644 --- a/core/prelude/operators/arithmetic.carbon +++ b/core/prelude/operators/arithmetic.carbon @@ -4,6 +4,8 @@ package Core library "prelude/operators/arithmetic"; +import library "prelude/types/int_literal"; + // Addition: `a + b`. interface Add { fn Op[self: Self](other: Self) -> Self; @@ -68,3 +70,30 @@ interface Mod { interface ModAssign { fn Op[addr self: Self*](other: Self); } + + +// Operations for IntLiteral. These need to be here because IntLiteral has no +// associated library of its own. +impl IntLiteral() as Add { + fn Op[self: Self](other: Self) -> Self = "int.sadd"; +} + +impl IntLiteral() as Div { + fn Op[self: Self](other: Self) -> Self = "int.sdiv"; +} + +impl IntLiteral() as Mod { + fn Op[self: Self](other: Self) -> Self = "int.smod"; +} + +impl IntLiteral() as Mul { + fn Op[self: Self](other: Self) -> Self = "int.smul"; +} + +impl IntLiteral() as Negate { + fn Op[self: Self]() -> Self = "int.snegate"; +} + +impl IntLiteral() as Sub { + fn Op[self: Self](other: Self) -> Self = "int.ssub"; +} diff --git a/core/prelude/operators/bitwise.carbon b/core/prelude/operators/bitwise.carbon index 30e665c294063..1eecda4b94b06 100644 --- a/core/prelude/operators/bitwise.carbon +++ b/core/prelude/operators/bitwise.carbon @@ -4,6 +4,8 @@ package Core library "prelude/operators/bitwise"; +import library "prelude/types/int_literal"; + // Bit complement: `^a`. interface BitComplement { fn Op[self: Self]() -> Self; @@ -58,3 +60,30 @@ interface RightShift { interface RightShiftAssign { fn Op[addr self: Self*](other: Self); } + + +// Operations for IntLiteral. These need to be here because IntLiteral has no +// associated library of its own. +impl IntLiteral() as BitAnd { + fn Op[self: Self](other: Self) -> Self = "int.and"; +} + +impl IntLiteral() as BitComplement { + fn Op[self: Self]() -> Self = "int.complement"; +} + +impl IntLiteral() as BitOr { + fn Op[self: Self](other: Self) -> Self = "int.or"; +} + +impl IntLiteral() as BitXor { + fn Op[self: Self](other: Self) -> Self = "int.xor"; +} + +impl IntLiteral() as LeftShift { + fn Op[self: Self](other: Self) -> Self = "int.left_shift"; +} + +impl IntLiteral() as RightShift { + fn Op[self: Self](other: Self) -> Self = "int.right_shift"; +} diff --git a/core/prelude/operators/comparison.carbon b/core/prelude/operators/comparison.carbon index 7a76a70361f83..45d48b3fbfa2e 100644 --- a/core/prelude/operators/comparison.carbon +++ b/core/prelude/operators/comparison.carbon @@ -5,6 +5,7 @@ package Core library "prelude/operators/comparison"; export import library "prelude/types/bool"; +import library "prelude/types/int_literal"; // Equality comparison: `a == b` and `a != b`. interface Eq { @@ -28,3 +29,19 @@ impl bool as Eq { fn Equal[self: Self](other: Self) -> bool = "bool.eq"; fn NotEqual[self: Self](other: Self) -> bool = "bool.neq"; } + + +// Operations for IntLiteral. These need to be here because IntLiteral has no +// associated library of its own. +impl IntLiteral() as Eq { + fn Equal[self: Self](other: Self) -> bool = "int.eq"; + fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; +} + +impl IntLiteral() as Ordered { + // TODO: fn Compare + fn Less[self: Self](other: Self) -> bool = "int.less"; + fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; + fn Greater[self: Self](other: Self) -> bool = "int.greater"; + fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; +} diff --git a/toolchain/base/int.h b/toolchain/base/int.h index 712afb3fa75c8..918180fa467b4 100644 --- a/toolchain/base/int.h +++ b/toolchain/base/int.h @@ -232,6 +232,14 @@ constexpr int32_t IntId::InvalidIndex = Invalid.AsIndex(); // an array of `APInt` values and represented as an index in the ID. class IntStore { public: + // The maximum supported bit width of an integer type. + // TODO: Pick a maximum size and document it in the design. For now + // we use 2^^23, because that's the largest size that LLVM supports. + static constexpr int MaxIntWidth = 1 << 23; + + // Pick a canonical bit width for the provided number of significant bits. + static auto CanonicalBitWidth(int significant_bits) -> int; + // Accepts a signed `int64_t` and uses the mathematical signed integer value // of it as the added integer value. // @@ -395,9 +403,6 @@ class IntStore { return IntId::Invalid; } - // Pick a canonical bit width for the provided number of significant bits. - static auto CanonicalBitWidth(int significant_bits) -> int; - // Canonicalize an incoming signed APInt to the correct bit width. static auto CanonicalizeSigned(llvm::APInt value) -> llvm::APInt; diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index bc118f8c19c80..aa25e17109603 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -667,17 +667,14 @@ static auto ValidateIntType(Context& context, SemIRLoc loc, {.type = bit_width->type_id, .value = bit_width_val}); return false; } - // TODO: Pick a maximum size and document it in the design. For now - // we use 2^^23, because that's the largest size that LLVM supports. - constexpr int MaxIntWidth = 1 << 23; - if (bit_width_val.ugt(MaxIntWidth)) { + if (bit_width_val.ugt(IntStore::MaxIntWidth)) { CARBON_DIAGNOSTIC(IntWidthTooLarge, Error, "integer type width of {0} is greater than the " "maximum supported width of {1}", TypedInt, int); context.emitter().Emit(loc, IntWidthTooLarge, {.type = bit_width->type_id, .value = bit_width_val}, - MaxIntWidth); + IntStore::MaxIntWidth); return false; } return true; @@ -769,6 +766,15 @@ static auto DiagnoseDivisionByZero(Context& context, SemIRLoc loc) -> void { context.emitter().Emit(loc, CompileTimeDivisionByZero); } +// Get an integer at a suitable bit-width: either `bit_width_id` if it is valid, +// or the canonical width from the value store if not. +static auto GetIntAtSuitableWidth(Context& context, IntId int_id, + IntId bit_width_id) -> llvm::APInt { + return bit_width_id.is_valid() + ? context.ints().GetAtWidth(int_id, bit_width_id) + : context.ints().Get(int_id); +} + // Performs a builtin unary integer -> integer operation. static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLoc loc, SemIR::BuiltinFunctionKind builtin_kind, @@ -777,24 +783,34 @@ static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLoc loc, auto op = context.insts().GetAs(arg_id); auto [is_signed, bit_width_id] = context.sem_ir().types().GetIntTypeInfo(op.type_id); - CARBON_CHECK(bit_width_id != IntId::Invalid, - "Cannot evaluate a generic bit width integer: {0}", op); - llvm::APInt op_val = context.ints().GetAtWidth(op.int_id, bit_width_id); + llvm::APInt op_val = GetIntAtSuitableWidth(context, op.int_id, bit_width_id); switch (builtin_kind) { case SemIR::BuiltinFunctionKind::IntSNegate: if (op_val.isMinSignedValue()) { - CARBON_DIAGNOSTIC(CompileTimeIntegerNegateOverflow, Error, - "integer overflow in negation of {0}", TypedInt); - context.emitter().Emit(loc, CompileTimeIntegerNegateOverflow, - {.type = op.type_id, .value = op_val}); + if (bit_width_id.is_valid()) { + CARBON_DIAGNOSTIC(CompileTimeIntegerNegateOverflow, Error, + "integer overflow in negation of {0}", TypedInt); + context.emitter().Emit(loc, CompileTimeIntegerNegateOverflow, + {.type = op.type_id, .value = op_val}); + } else { + // Widen the integer so we don't overflow into the sign bit. + op_val = op_val.sext(op_val.getBitWidth() + + llvm::APInt::APINT_BITS_PER_WORD); + } } op_val.negate(); break; case SemIR::BuiltinFunctionKind::IntUNegate: + CARBON_CHECK(bit_width_id.is_valid(), "Unsigned negate on unsized int"); op_val.negate(); break; case SemIR::BuiltinFunctionKind::IntComplement: + // TODO: Should we have separate builtins for signed and unsigned + // complement? Like with signed/unsigned negate, these operations do + // different things to the integer value, even though they do the same + // thing to the bits. We treat IntLiteral complement as signed complement, + // given that the result of unsigned complement depends on the bit width. op_val.flipAllBits(); break; default: @@ -804,80 +820,53 @@ static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLoc loc, return MakeIntResult(context, op.type_id, is_signed, std::move(op_val)); } -// Performs a builtin binary integer -> integer operation. -static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc 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); - - // Check for division by zero. - switch (builtin_kind) { - case SemIR::BuiltinFunctionKind::IntSDiv: - case SemIR::BuiltinFunctionKind::IntSMod: - case SemIR::BuiltinFunctionKind::IntUDiv: - case SemIR::BuiltinFunctionKind::IntUMod: - if (context.ints().Get(rhs.int_id).isZero()) { - DiagnoseDivisionByZero(context, loc); - return SemIR::ErrorInst::SingletonConstantId; - } - break; - default: - break; - } - - auto [lhs_is_signed, lhs_bit_width_id] = - context.sem_ir().types().GetIntTypeInfo(lhs.type_id); - llvm::APInt lhs_val = context.ints().GetAtWidth(lhs.int_id, lhs_bit_width_id); - - llvm::APInt result_val; - - // First handle shift, which can directly use the canonical RHS and doesn't - // overflow. - switch (builtin_kind) { - // Bit shift. - case SemIR::BuiltinFunctionKind::IntLeftShift: - case SemIR::BuiltinFunctionKind::IntRightShift: { - const auto& rhs_orig_val = context.ints().Get(rhs.int_id); - if (rhs_orig_val.uge(lhs_val.getBitWidth()) || - (rhs_orig_val.isNegative() && lhs_is_signed)) { - CARBON_DIAGNOSTIC( - CompileTimeShiftOutOfRange, Error, - "shift distance not in range [0, {0}) in {1} {2:<<|>>} {3}", - unsigned, TypedInt, BoolAsSelect, TypedInt); - context.emitter().Emit( - loc, CompileTimeShiftOutOfRange, lhs_val.getBitWidth(), - {.type = lhs.type_id, .value = lhs_val}, - builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift, - {.type = rhs.type_id, .value = rhs_orig_val}); - // TODO: Is it useful to recover by returning 0 or -1? - return SemIR::ErrorInst::SingletonConstantId; - } +namespace { +// A pair of APInts that are the operands of a binary operator. We use an +// aggregate rather than `std::pair` to allow RVO of the individual ints. +struct APIntBinaryOperands { + llvm::APInt lhs; + llvm::APInt rhs; +}; +} // namespace - if (builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift) { - result_val = lhs_val.shl(rhs_orig_val); - } else if (lhs_is_signed) { - result_val = lhs_val.ashr(rhs_orig_val); +// Get a pair of integers at the same suitable bit-width: either their actual +// width if they have a fixed width, or the smallest canonical width in which +// they both fit otherwise. +static auto GetIntsAtSuitableWidth(Context& context, IntId lhs_id, IntId rhs_id, + IntId bit_width_id) -> APIntBinaryOperands { + // Unsized operands: take the wider of the bit widths. + if (!bit_width_id.is_valid()) { + APIntBinaryOperands result = {.lhs = context.ints().Get(lhs_id), + .rhs = context.ints().Get(rhs_id)}; + if (result.lhs.getBitWidth() != result.rhs.getBitWidth()) { + if (result.lhs.getBitWidth() > result.rhs.getBitWidth()) { + result.rhs = result.rhs.sext(result.lhs.getBitWidth()); } else { - result_val = lhs_val.lshr(rhs_orig_val); + result.lhs = result.lhs.sext(result.rhs.getBitWidth()); } - return MakeIntResult(context, lhs.type_id, lhs_is_signed, - std::move(result_val)); } - - default: - // Break to do additional setup for other builtin kinds. - break; + return result; } - // Other operations are already checked to be homogeneous, so we can extend - // the RHS with the LHS bit width. - CARBON_CHECK(rhs.type_id == lhs.type_id, "Heterogeneous builtin integer op!"); - llvm::APInt rhs_val = context.ints().GetAtWidth(rhs.int_id, lhs_bit_width_id); + return {.lhs = context.ints().GetAtWidth(lhs_id, bit_width_id), + .rhs = context.ints().GetAtWidth(rhs_id, bit_width_id)}; +} + +namespace { +// The result of performing a binary int operation. +struct BinaryIntOpResult { + llvm::APInt result_val; + bool overflow; + Lex::TokenKind op_token; +}; +} // namespace - // We may also need to diagnose overflow for these operations. +// Computes the result of a homogeneous binary (int, int) -> int operation. +static auto ComputeBinaryIntOpResult(SemIR::BuiltinFunctionKind builtin_kind, + const llvm::APInt& lhs_val, + const llvm::APInt& rhs_val) + -> BinaryIntOpResult { + llvm::APInt result_val; bool overflow = false; Lex::TokenKind op_token = Lex::TokenKind::Not; @@ -943,23 +932,165 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc, case SemIR::BuiltinFunctionKind::IntLeftShift: case SemIR::BuiltinFunctionKind::IntRightShift: - CARBON_FATAL("Handled specially above."); + CARBON_FATAL("Non-homogeneous operation handled separately."); default: CARBON_FATAL("Unexpected operation kind."); } + return {.result_val = std::move(result_val), + .overflow = overflow, + .op_token = op_token}; +} + +// Performs a builtin integer bit shift operation. +static auto PerformBuiltinIntShiftOp(Context& context, SemIRLoc 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_is_signed, lhs_bit_width_id] = + context.sem_ir().types().GetIntTypeInfo(lhs.type_id); + + llvm::APInt lhs_val = + GetIntAtSuitableWidth(context, lhs.int_id, lhs_bit_width_id); + const auto& rhs_orig_val = context.ints().Get(rhs.int_id); + if (lhs_bit_width_id.is_valid() && rhs_orig_val.uge(lhs_val.getBitWidth())) { + CARBON_DIAGNOSTIC( + CompileTimeShiftOutOfRange, Error, + "shift distance >= type width of {0} in `{1} {2:<<|>>} {3}`", unsigned, + TypedInt, BoolAsSelect, TypedInt); + context.emitter().Emit( + loc, CompileTimeShiftOutOfRange, lhs_val.getBitWidth(), + {.type = lhs.type_id, .value = lhs_val}, + builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift, + {.type = rhs.type_id, .value = rhs_orig_val}); + // TODO: Is it useful to recover by returning 0 or -1? + return SemIR::ErrorInst::SingletonConstantId; + } + + if (rhs_orig_val.isNegative() && + context.sem_ir().types().IsSignedInt(rhs.type_id)) { + CARBON_DIAGNOSTIC(CompileTimeShiftNegative, Error, + "shift distance negative in `{0} {1:<<|>>} {2}`", + TypedInt, BoolAsSelect, TypedInt); + context.emitter().Emit( + loc, CompileTimeShiftNegative, {.type = lhs.type_id, .value = lhs_val}, + builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift, + {.type = rhs.type_id, .value = rhs_orig_val}); + // TODO: Is it useful to recover by returning 0 or -1? + return SemIR::ErrorInst::SingletonConstantId; + } + + llvm::APInt result_val; + if (builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift) { + if (!lhs_bit_width_id.is_valid() && !lhs_val.isZero()) { + // Ensure we don't generate a ridiculously large integer through a bit + // shift. + auto width = rhs_orig_val.trySExtValue(); + if (!width || + *width > IntStore::MaxIntWidth - lhs_val.getSignificantBits()) { + CARBON_DIAGNOSTIC(CompileTimeUnsizedShiftOutOfRange, Error, + "shift distance of {0} would result in an " + "integer whose width is greater than the " + "maximum supported width of {1}", + TypedInt, int); + context.emitter().Emit(loc, CompileTimeUnsizedShiftOutOfRange, + {.type = rhs.type_id, .value = rhs_orig_val}, + IntStore::MaxIntWidth); + return SemIR::ErrorInst::SingletonConstantId; + } + lhs_val = lhs_val.sext( + IntStore::CanonicalBitWidth(lhs_val.getSignificantBits() + *width)); + } + + result_val = + lhs_val.shl(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); + } else if (lhs_is_signed) { + result_val = + lhs_val.ashr(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); + } else { + CARBON_CHECK(lhs_bit_width_id.is_valid(), "Logical shift on unsized int"); + result_val = + lhs_val.lshr(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); + } + return MakeIntResult(context, lhs.type_id, lhs_is_signed, + std::move(result_val)); +} + +// Performs a homogeneous builtin binary integer -> integer operation. +static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc 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); + + CARBON_CHECK(rhs.type_id == lhs.type_id, "Heterogeneous builtin integer op!"); + auto type_id = lhs.type_id; + auto [is_signed, bit_width_id] = + context.sem_ir().types().GetIntTypeInfo(type_id); + auto [lhs_val, rhs_val] = + GetIntsAtSuitableWidth(context, lhs.int_id, rhs.int_id, bit_width_id); + + // Check for division by zero. + switch (builtin_kind) { + case SemIR::BuiltinFunctionKind::IntSDiv: + case SemIR::BuiltinFunctionKind::IntSMod: + case SemIR::BuiltinFunctionKind::IntUDiv: + case SemIR::BuiltinFunctionKind::IntUMod: + if (rhs_val.isZero()) { + DiagnoseDivisionByZero(context, loc); + return SemIR::ErrorInst::SingletonConstantId; + } + break; + default: + break; + } + + BinaryIntOpResult result = + ComputeBinaryIntOpResult(builtin_kind, lhs_val, rhs_val); + + if (result.overflow && !bit_width_id.is_valid()) { + // Retry with a larger bit width. Most operations can only overflow by one + // bit, but signed n-bit multiplication can overflow to 2n-1 bits. We don't + // need to handle unsigned multiplication here because it's not permitted + // for unsized integers. + // + // Note that we speculatively first perform the calculation in the width of + // the wider operand: smaller operations are faster and overflow to a wider + // integer is unlikely to be needed, especially given that the width will + // have been rounded up to a multiple of 64 bits by the int store. + CARBON_CHECK(builtin_kind != SemIR::BuiltinFunctionKind::IntUMul, + "Unsigned arithmetic requires a fixed bitwidth"); + int new_width = + builtin_kind == SemIR::BuiltinFunctionKind::IntSMul + ? lhs_val.getBitWidth() * 2 + : IntStore::CanonicalBitWidth(lhs_val.getBitWidth() + 1); + new_width = std::min(new_width, IntStore::MaxIntWidth); + lhs_val = context.ints().GetAtWidth(lhs.int_id, new_width); + rhs_val = context.ints().GetAtWidth(rhs.int_id, new_width); + + // Note that this can in theory still overflow if we limited `new_width` to + // `MaxIntWidth`. In that case we fall through to the signed overflow + // diagnostic below. + result = ComputeBinaryIntOpResult(builtin_kind, lhs_val, rhs_val); + CARBON_CHECK(!result.overflow || new_width == IntStore::MaxIntWidth); + } - if (overflow) { + if (result.overflow) { CARBON_DIAGNOSTIC(CompileTimeIntegerOverflow, Error, - "integer overflow in calculation {0} {1} {2}", TypedInt, + "integer overflow in calculation `{0} {1} {2}`", TypedInt, Lex::TokenKind, TypedInt); context.emitter().Emit(loc, CompileTimeIntegerOverflow, - {.type = lhs.type_id, .value = lhs_val}, op_token, - {.type = rhs.type_id, .value = rhs_val}); + {.type = type_id, .value = lhs_val}, result.op_token, + {.type = type_id, .value = rhs_val}); } - return MakeIntResult(context, lhs.type_id, lhs_is_signed, - std::move(result_val)); + return MakeIntResult(context, type_id, is_signed, + std::move(result.result_val)); } // Performs a builtin integer comparison. @@ -971,15 +1102,8 @@ static auto PerformBuiltinIntComparison(Context& context, -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); - CARBON_CHECK(lhs.type_id == rhs.type_id, - "Builtin comparison with mismatched types!"); - - auto [is_signed, bit_width_id] = - context.sem_ir().types().GetIntTypeInfo(lhs.type_id); - CARBON_CHECK(bit_width_id != IntId::Invalid, - "Cannot evaluate a generic bit width integer: {0}", lhs); - llvm::APInt lhs_val = context.ints().GetAtWidth(lhs.int_id, bit_width_id); - llvm::APInt rhs_val = context.ints().GetAtWidth(rhs.int_id, bit_width_id); + llvm::APInt lhs_val = context.ints().Get(lhs.int_id); + llvm::APInt rhs_val = context.ints().Get(rhs.int_id); bool result; switch (builtin_kind) { @@ -990,16 +1114,16 @@ static auto PerformBuiltinIntComparison(Context& context, result = (lhs_val != rhs_val); break; case SemIR::BuiltinFunctionKind::IntLess: - result = is_signed ? lhs_val.slt(rhs_val) : lhs_val.ult(rhs_val); + result = lhs_val.slt(rhs_val); break; case SemIR::BuiltinFunctionKind::IntLessEq: - result = is_signed ? lhs_val.sle(rhs_val) : lhs_val.ule(rhs_val); + result = lhs_val.sle(rhs_val); break; case SemIR::BuiltinFunctionKind::IntGreater: - result = is_signed ? lhs_val.sgt(rhs_val) : lhs_val.sgt(rhs_val); + result = lhs_val.sgt(rhs_val); break; case SemIR::BuiltinFunctionKind::IntGreaterEq: - result = is_signed ? lhs_val.sge(rhs_val) : lhs_val.sge(rhs_val); + result = lhs_val.sge(rhs_val); break; default: CARBON_FATAL("Unexpected operation kind."); @@ -1176,7 +1300,7 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, return PerformBuiltinUnaryIntOp(context, loc, builtin_kind, arg_ids[0]); } - // Binary integer -> integer operations. + // Homogeneous binary integer -> integer operations. case SemIR::BuiltinFunctionKind::IntSAdd: case SemIR::BuiltinFunctionKind::IntSSub: case SemIR::BuiltinFunctionKind::IntSMul: @@ -1189,9 +1313,7 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, case SemIR::BuiltinFunctionKind::IntUMod: case SemIR::BuiltinFunctionKind::IntAnd: case SemIR::BuiltinFunctionKind::IntOr: - case SemIR::BuiltinFunctionKind::IntXor: - case SemIR::BuiltinFunctionKind::IntLeftShift: - case SemIR::BuiltinFunctionKind::IntRightShift: { + case SemIR::BuiltinFunctionKind::IntXor: { if (phase != Phase::Template) { break; } @@ -1199,6 +1321,16 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc, arg_ids[1]); } + // Bit shift operations. + case SemIR::BuiltinFunctionKind::IntLeftShift: + case SemIR::BuiltinFunctionKind::IntRightShift: { + if (phase != Phase::Template) { + break; + } + return PerformBuiltinIntShiftOp(context, loc, builtin_kind, arg_ids[0], + arg_ids[1]); + } + // Integer comparisons. case SemIR::BuiltinFunctionKind::IntEq: case SemIR::BuiltinFunctionKind::IntNeq: @@ -1311,7 +1443,9 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc, // If any operand of the call is non-constant, the call is non-constant. // TODO: Some builtin calls might allow some operands to be non-constant. if (!has_constant_operands) { - if (builtin_kind.IsCompTimeOnly()) { + if (builtin_kind.IsCompTimeOnly( + eval_context.sem_ir(), eval_context.inst_blocks().Get(call.args_id), + call.type_id)) { CARBON_DIAGNOSTIC(NonConstantCallToCompTimeOnlyFunction, Error, "non-constant call to compile-time-only function"); CARBON_DIAGNOSTIC(CompTimeOnlyFunctionHere, Note, diff --git a/toolchain/check/testdata/array/base.carbon b/toolchain/check/testdata/array/base.carbon index f186bef623c60..1bde258854861 100644 --- a/toolchain/check/testdata/array/base.carbon +++ b/toolchain/check/testdata/array/base.carbon @@ -49,7 +49,7 @@ var c: [(); 5] = ((), (), (), (), (),); // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Float = %import_ref.193 +// CHECK:STDOUT: .Float = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/builtin_types.carbon b/toolchain/check/testdata/basics/builtin_types.carbon index a049b21d2cfcb..3937cb3a97156 100644 --- a/toolchain/check/testdata/basics/builtin_types.carbon +++ b/toolchain/check/testdata/basics/builtin_types.carbon @@ -34,7 +34,7 @@ var test_type: type = i32; // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Float = %import_ref.193 +// CHECK:STDOUT: .Float = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon b/toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon index 454ace1a461b8..a5952ca460bac 100644 --- a/toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon +++ b/toolchain/check/testdata/basics/fail_numeric_literal_overflow.carbon @@ -60,7 +60,7 @@ let e: f64 = 5.0e39999999999999999993; // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Float = %import_ref.193 +// CHECK:STDOUT: .Float = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/basics/numeric_literals.carbon b/toolchain/check/testdata/basics/numeric_literals.carbon index 90f6965dfedf3..110e85d0d1529 100644 --- a/toolchain/check/testdata/basics/numeric_literals.carbon +++ b/toolchain/check/testdata/basics/numeric_literals.carbon @@ -77,7 +77,7 @@ fn F() { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Float = %import_ref.193 +// CHECK:STDOUT: .Float = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/builtins/float/make_type.carbon b/toolchain/check/testdata/builtins/float/make_type.carbon index 26b97383e7c95..1148e06d10954 100644 --- a/toolchain/check/testdata/builtins/float/make_type.carbon +++ b/toolchain/check/testdata/builtins/float/make_type.carbon @@ -102,7 +102,7 @@ var dyn: Float(dyn_size); // CHECK:STDOUT: %import_ref.1: %Float.type = import_ref Main//types, Float, loaded [template = constants.%Float] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Int = %import_ref.193 +// CHECK:STDOUT: .Int = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -176,7 +176,7 @@ var dyn: Float(dyn_size); // CHECK:STDOUT: %import_ref.1: %Float.type = import_ref Main//types, Float, loaded [template = constants.%Float] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Int = %import_ref.193 +// CHECK:STDOUT: .Int = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/builtins/int/and.carbon b/toolchain/check/testdata/builtins/int/and.carbon index 325f6f0ff821d..944f35971a047 100644 --- a/toolchain/check/testdata/builtins/int/and.carbon +++ b/toolchain/check/testdata/builtins/int/and.carbon @@ -12,6 +12,8 @@ // --- int_and.carbon +library "[[@TEST_NAME]]"; + fn And(a: i32, b: i32) -> i32 = "int.and"; var arr: [i32; And(12, 10)]; @@ -20,3 +22,100 @@ let arr_p: [i32; 8]* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return And(a, b); } + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(And(1, 2)) as Expect(0); + Test(And(12, 10)) as Expect(8); + + Test(And(1, -1)) as Expect(1); + Test(And(-2, -3)) as Expect(-4); + // Ensure the sign bit is treated properly even for 64-bit numbers. + Test(And(0x7FFF_FFFF_FFFF_FFFF, -3)) as Expect(0x7FFF_FFFF_FFFF_FFFD); + Test(And(0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); +} + +// --- fail_literal_runtime.carbon + +library "[[@TEST_NAME]]"; + +fn AndLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + +fn F(a: Core.IntLiteral()) -> Core.IntLiteral() { + // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE+7]]:10: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: return AndLit(5, a); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE-6]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn AndLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return AndLit(5, a); +} + +// --- fail_bad_decl.carbon + +library "[[@TEST_NAME]]"; + +// Heterogeneous "and" is not supported. +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAnd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.and"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAnd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.and"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAnd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.and"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAnd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.and"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAnd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAnd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAnd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.and"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAnd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.and"; + +// --- fail_runtime_literal.carbon + +library "[[@TEST_NAME]]"; + +fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + +fn Test(n: Core.IntLiteral()) { + // OK + And(1, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: And(n, 1); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + And(n, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: And(1, n); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + And(1, n); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+6]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: And(n, n); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + And(n, n); +} diff --git a/toolchain/check/testdata/builtins/int/complement.carbon b/toolchain/check/testdata/builtins/int/complement.carbon index ddcb047d88f73..7fddbd691d72b 100644 --- a/toolchain/check/testdata/builtins/int/complement.carbon +++ b/toolchain/check/testdata/builtins/int/complement.carbon @@ -12,6 +12,8 @@ // --- int_complement.carbon +library "[[@TEST_NAME]]"; + fn Complement(a: i32) -> i32 = "int.complement"; fn And(a: i32, b: i32) -> i32 = "int.and"; @@ -21,3 +23,38 @@ let arr_p: [i32; 0xEDCBA9]* = &arr; fn RuntimeCallIsValid(a: i32) -> i32 { return Complement(a); } + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(Complement(0)) as Expect(-1); + Test(Complement(1)) as Expect(-2); + Test(Complement(-1)) as Expect(0); + Test(Complement(-0x7FFF_FFFF_FFFF_FFFF)) as Expect(0x7FFF_FFFF_FFFF_FFFE); + Test(Complement(-0x8000_0000_0000_0000)) as Expect(0x7FFF_FFFF_FFFF_FFFF); + Test(Complement(0x7FFF_FFFF_FFFF_FFFF)) as Expect(-0x8000_0000_0000_0000); + Test(Complement(0x8000_0000_0000_0000)) as Expect(-0x8000_0000_0000_0001); +} + +// --- fail_literal_runtime.carbon + +library "[[@TEST_NAME]]"; + +fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; + +fn F(a: Core.IntLiteral()) -> Core.IntLiteral() { + // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE+6]]:10: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: return Complement(a); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE-6]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + return Complement(a); +} diff --git a/toolchain/check/testdata/builtins/int/eq.carbon b/toolchain/check/testdata/builtins/int/eq.carbon index 2700539af9293..efea564018822 100644 --- a/toolchain/check/testdata/builtins/int/eq.carbon +++ b/toolchain/check/testdata/builtins/int/eq.carbon @@ -12,6 +12,8 @@ // --- int_eq.carbon +library "[[@TEST_NAME]]"; + fn Eq(a: i32, b: i32) -> bool = "int.eq"; class True {} @@ -30,7 +32,74 @@ fn RuntimeCallIsValid(a: i32, b: i32) -> bool { package FailBadDecl; -// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+3]]:1: error: invalid signature for builtin function "int.eq" [InvalidBuiltinSignature] +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.eq" [InvalidBuiltinSignature] // CHECK:STDERR: fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(Eq(5, 5)) as Expect(true); + Test(Eq(5, 6)) as Expect(false); + Test(Eq(-1, -1)) as Expect(true); + Test(Eq(-1, 1)) as Expect(false); +} + +// --- mixed.carbon + +library "[[@TEST_NAME]]"; + +fn Eq(a: Core.IntLiteral(), b: i32) -> bool = "int.eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(Eq(5, 5)) as Expect(true); + Test(Eq(5, 6)) as Expect(false); + Test(Eq(-1, -1)) as Expect(true); + Test(Eq(-1, 1)) as Expect(false); +} + +// --- fail_runtime_literal.carbon + +library "[[@TEST_NAME]]"; + +fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; + +fn Test(n: Core.IntLiteral()) { + // OK + Eq(1, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: Eq(n, 1); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Eq(n, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: Eq(1, n); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Eq(1, n); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+6]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: Eq(n, n); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Eq(n, n); +} diff --git a/toolchain/check/testdata/builtins/int/greater_eq.carbon b/toolchain/check/testdata/builtins/int/greater_eq.carbon index 952e906458a0a..54e978d3a40f7 100644 --- a/toolchain/check/testdata/builtins/int/greater_eq.carbon +++ b/toolchain/check/testdata/builtins/int/greater_eq.carbon @@ -12,6 +12,8 @@ // --- int_greater_eq.carbon +library "[[@TEST_NAME]]"; + fn GreaterEq(a: i32, b: i32) -> bool = "int.greater_eq"; fn Negate(a: i32) -> i32 = "int.snegate"; @@ -29,3 +31,39 @@ fn F(true_: True, false_: False) { fn RuntimeCallIsValid(a: i32, b: i32) -> bool { return GreaterEq(a, b); } + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn GreaterEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.greater_eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(GreaterEq(5, 5)) as Expect(true); + Test(GreaterEq(5, 6)) as Expect(false); + Test(GreaterEq(6, 5)) as Expect(true); + Test(GreaterEq(-1, -1)) as Expect(true); + Test(GreaterEq(-1, 1)) as Expect(false); + Test(GreaterEq(1, -1)) as Expect(true); +} + +// --- mixed.carbon + +library "[[@TEST_NAME]]"; + +fn GreaterEq(a: Core.IntLiteral(), b: i32) -> bool = "int.greater_eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(GreaterEq(5, 5)) as Expect(true); + Test(GreaterEq(5, 6)) as Expect(false); + Test(GreaterEq(6, 5)) as Expect(true); + Test(GreaterEq(-1, -1)) as Expect(true); + Test(GreaterEq(-1, 1)) as Expect(false); + Test(GreaterEq(1, -1)) as Expect(true); +} diff --git a/toolchain/check/testdata/builtins/int/left_shift.carbon b/toolchain/check/testdata/builtins/int/left_shift.carbon index 47a7036515f12..b749abe4942fa 100644 --- a/toolchain/check/testdata/builtins/int/left_shift.carbon +++ b/toolchain/check/testdata/builtins/int/left_shift.carbon @@ -10,34 +10,109 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/left_shift.carbon -// --- int_left_shift.carbon +// --- i32.carbon + +library "[[@TEST_NAME]]"; + +class Expect(N:! i32) {} +fn Test(N:! i32) -> Expect(N) { return {}; } fn LeftShift(a: i32, b: i32) -> i32 = "int.left_shift"; -var arr: [i32; LeftShift(5, 2)]; -let arr_p: [i32; 20]* = &arr; +fn F() { + Test(LeftShift(0, 0)) as Expect(0); + Test(LeftShift(0, 1)) as Expect(0); + Test(LeftShift(0, 30)) as Expect(0); + Test(LeftShift(1, 30)) as Expect(0x4000_0000); + Test(LeftShift(5, 2)) as Expect(20); + Test(LeftShift(-1, 1)) as Expect(-2); + Test(LeftShift(-2, 1)) as Expect(-4); + Test(LeftShift(-3, 1)) as Expect(-6); +} fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return LeftShift(a, b); } -// TODO: Test mixed types for LHS and RHS. +// --- u32.carbon + +library "[[@TEST_NAME]]"; + +class Expect(N:! u32) {} +fn Test(N:! u32) -> Expect(N) { return {}; } + +fn LeftShift(a: u32, b: i32) -> u32 = "int.left_shift"; + +fn F() { + Test(LeftShift(0, 0)) as Expect(0); + Test(LeftShift(0, 1)) as Expect(0); + Test(LeftShift(0, 30)) as Expect(0); + Test(LeftShift(1, 30)) as Expect(0x4000_0000); + Test(LeftShift(5, 2)) as Expect(20); + Test(LeftShift(0xFFFF_FFFF, 1)) as Expect(0xFFFF_FFFE); + Test(LeftShift(0xFFFF_FFFE, 1)) as Expect(0xFFFF_FFFC); + Test(LeftShift(0xABCD_EF01, 8)) as Expect(0xCDEF_0100); +} + +fn RuntimeCall(a: u32, b: i32) -> u32 { + return LeftShift(a, b); +} + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn LeftShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.left_shift"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + // Zero can be shifted by any amount. + Test(LeftShift(0, 0)) as Expect(0); + Test(LeftShift(0, 0)) as Expect(0); + Test(LeftShift(0, 1)) as Expect(0); + Test(LeftShift(0, 30)) as Expect(0); + Test(LeftShift(0, 1_000_000_000)) as Expect(0); + + // Positive numbers can be shifted. + Test(LeftShift(1, 0)) as Expect(1); + Test(LeftShift(1, 1)) as Expect(2); + Test(LeftShift(2, 1)) as Expect(4); + Test(LeftShift(1, 2)) as Expect(4); + Test(LeftShift(3, 2)) as Expect(12); + Test(LeftShift(1, 30)) as Expect(0x4000_0000); + Test(LeftShift(5, 2)) as Expect(20); + + // Negative numbers can be shifted too. + Test(LeftShift(-1, 0)) as Expect(-1); + Test(LeftShift(-1, 1)) as Expect(-2); + Test(LeftShift(-2, 1)) as Expect(-4); + Test(LeftShift(-3, 1)) as Expect(-6); + + // Large numbers can be shifted losslessly. + Test(LeftShift(0xFFFF_FFFF, 1)) as Expect(0x1_FFFF_FFFE); + Test(LeftShift(0xFFFF_FFFE, 1)) as Expect(0x1_FFFF_FFFC); + Test(LeftShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF_0100); + Test(LeftShift(0x7FFF_FFFF_FFFF_FFFF, 1)) as Expect(0xFFFF_FFFF_FFFF_FFFE); + Test(LeftShift(0xFFFF_FFFF_FFFF_FFFF, 1)) as Expect(0x1_FFFF_FFFF_FFFF_FFFE); +} // --- fail_bad_shift.carbon -package BadShift; +library "[[@TEST_NAME]]"; fn LeftShift(a: i32, b: i32) -> i32 = "int.left_shift"; -fn Negate(a: i32) -> i32 = "int.snegate"; +fn LeftShiftLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; -// Shift greater than size is disallowed. +// Shift greater than size is disallowed for sized types. let size_1: i32 = LeftShift(1, 31); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance not in range [0, 32) in 1 << 32 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_2: i32 = LeftShift(1, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_2: i32 = LeftShift(1, 32); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance not in range [0, 32) in 1 << 33 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 << 33` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_3: i32 = LeftShift(1, 33); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -45,7 +120,7 @@ let size_3: i32 = LeftShift(1, 33); // Overflow is allowed if the shift distance is in bounds. let overflow_1: i32 = LeftShift(1000, 31); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:23: error: shift distance not in range [0, 32) in 1000 << 32 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:23: error: shift distance >= type width of 32 in `1000 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let overflow_2: i32 = LeftShift(1000, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -53,14 +128,97 @@ let overflow_2: i32 = LeftShift(1000, 32); // Oversize shifts aren't allowed even if there's no overflow. let no_overflow_1: i32 = LeftShift(0, 31); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance not in range [0, 32) in 0 << 32 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let no_overflow_2: i32 = LeftShift(0, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let no_overflow_2: i32 = LeftShift(0, 32); -// Negative shifts aren't allowed either. -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+3]]:21: error: shift distance not in range [0, 32) in 1 << -1 [CompileTimeShiftOutOfRange] -// CHECK:STDERR: let negative: i32 = LeftShift(1, Negate(1)); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ -let negative: i32 = LeftShift(1, Negate(1)); +// Negative shifts aren't allowed either, even for literals, even if the lhs is zero. +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:21: error: shift distance >= type width of 32 in `1 << -1` [CompileTimeShiftOutOfRange] +// CHECK:STDERR: let negative: i32 = LeftShift(1, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative: i32 = LeftShift(1, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 << -1` [CompileTimeShiftOutOfRange] +// CHECK:STDERR: let negative_zero: i32 = LeftShift(0, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative_zero: i32 = LeftShift(0, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:39: error: shift distance negative in `1 << -1` [CompileTimeShiftNegative] +// CHECK:STDERR: let negative_lit: Core.IntLiteral() = LeftShiftLit(1, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative_lit: Core.IntLiteral() = LeftShiftLit(1, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:44: error: shift distance negative in `0 << -1` [CompileTimeShiftNegative] +// CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = LeftShiftLit(0, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative_lit_zero: Core.IntLiteral() = LeftShiftLit(0, -1); + +// --- fail_literal_overflow.carbon + +library "[[@TEST_NAME]]"; + +fn LeftShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.left_shift"; + +// CHECK:STDERR: fail_literal_overflow.carbon:[[@LINE+4]]:16: error: shift distance of 1000000000 would result in an integer whose width is greater than the maximum supported width of 8388608 [CompileTimeUnsizedShiftOutOfRange] +// CHECK:STDERR: let bad: i32 = LeftShift(1, 1_000_000_000); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let bad: i32 = LeftShift(1, 1_000_000_000); + +// CHECK:STDERR: fail_literal_overflow.carbon:[[@LINE+4]]:25: error: shift distance of 1000000000 would result in an integer whose width is greater than the maximum supported width of 8388608 [CompileTimeUnsizedShiftOutOfRange] +// CHECK:STDERR: let bad_negative: i32 = LeftShift(-1, 1_000_000_000); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let bad_negative: i32 = LeftShift(-1, 1_000_000_000); + +// --- fail_comp_time_only_shift.carbon + +library "[[@TEST_NAME]]"; + +fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; +fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; + +var a_lit: Core.IntLiteral() = 12; +var an_i32: i32 = 34; + +// This can't be valid: we don't have a compile-time or runtime integer value for `a_lit`. +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:17: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] +// CHECK:STDERR: let bad1: i32 = LeftShiftByLit(an_i32, a_lit); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-10]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] +// CHECK:STDERR: fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let bad1: i32 = LeftShiftByLit(an_i32, a_lit); + +// TODO: This could be valid because we don't actually need the return value at runtime. +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:31: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] +// CHECK:STDERR: let bad2: Core.IntLiteral() = LeftShiftOfLit(a_lit, an_i32); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-19]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] +// CHECK:STDERR: fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let bad2: Core.IntLiteral() = LeftShiftOfLit(a_lit, an_i32); + +// TODO: This could be valid because the literal argument has a constant value. +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:17: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] +// CHECK:STDERR: let bad3: i32 = LeftShiftByLit(an_i32, 12); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-30]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] +// CHECK:STDERR: fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let bad3: i32 = LeftShiftByLit(an_i32, 12); + +// TODO: This could be valid because we don't actually need the return value at runtime. +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+6]]:31: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] +// CHECK:STDERR: let bad4: Core.IntLiteral() = LeftShiftOfLit(12, an_i32); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-39]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] +// CHECK:STDERR: fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let bad4: Core.IntLiteral() = LeftShiftOfLit(12, an_i32); diff --git a/toolchain/check/testdata/builtins/int/less_eq.carbon b/toolchain/check/testdata/builtins/int/less_eq.carbon index 6ae44f2efd2d0..7b170bfd06dea 100644 --- a/toolchain/check/testdata/builtins/int/less_eq.carbon +++ b/toolchain/check/testdata/builtins/int/less_eq.carbon @@ -12,6 +12,8 @@ // --- int_less_eq.carbon +library "[[@TEST_NAME]]"; + fn LessEq(a: i32, b: i32) -> bool = "int.less_eq"; fn Negate(a: i32) -> i32 = "int.snegate"; @@ -29,3 +31,73 @@ fn F(true_: True, false_: False) { fn RuntimeCallIsValid(a: i32, b: i32) -> bool { return LessEq(a, b); } + +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(LessEq(5, 5)) as Expect(true); + Test(LessEq(5, 6)) as Expect(true); + Test(LessEq(6, 5)) as Expect(false); + Test(LessEq(-1, -1)) as Expect(true); + Test(LessEq(-1, 1)) as Expect(true); + Test(LessEq(1, -1)) as Expect(false); +} + +// --- mixed.carbon + +library "[[@TEST_NAME]]"; + +fn LessEq(a: Core.IntLiteral(), b: i32) -> bool = "int.less_eq"; + +class Expect(B:! bool) {} +fn Test(B:! bool) -> Expect(B) { return {}; } + +fn F() { + Test(LessEq(5, 5)) as Expect(true); + Test(LessEq(5, 6)) as Expect(true); + Test(LessEq(6, 5)) as Expect(false); + Test(LessEq(-1, -1)) as Expect(true); + Test(LessEq(-1, 1)) as Expect(true); + Test(LessEq(1, -1)) as Expect(false); +} + +// --- fail_runtime_literal.carbon + +library "[[@TEST_NAME]]"; + +fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; + +fn Test(n: Core.IntLiteral()) { + // OK + LessEq(1, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: LessEq(n, 1); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + LessEq(n, 1); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: LessEq(1, n); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + LessEq(1, n); + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+6]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] + // CHECK:STDERR: LessEq(n, n); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] + // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LessEq(n, n); +} diff --git a/toolchain/check/testdata/builtins/int/right_shift.carbon b/toolchain/check/testdata/builtins/int/right_shift.carbon index ead0fec1966a4..6581455108fb6 100644 --- a/toolchain/check/testdata/builtins/int/right_shift.carbon +++ b/toolchain/check/testdata/builtins/int/right_shift.carbon @@ -10,58 +10,119 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/right_shift.carbon -// --- int_right_shift.carbon +// --- i32.carbon + +library "[[@TEST_NAME]]"; + +class Expect(N:! i32) {} +fn Test(N:! i32) -> Expect(N) { return {}; } fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; -var arr: [i32; RightShift(22, 2)]; -let arr_p: [i32; 5]* = &arr; +fn F() { + Test(RightShift(0, 31)) as Expect(0); + Test(RightShift(1, 31)) as Expect(0); + Test(RightShift(1, 0)) as Expect(1); + Test(RightShift(1, 2)) as Expect(0); + Test(RightShift(22, 2)) as Expect(5); + Test(RightShift(-1, 1)) as Expect(-1); + Test(RightShift(-2, 1)) as Expect(-1); + Test(RightShift(-10, 2)) as Expect(-3); +} fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return RightShift(a, b); } -// TODO: Test mixed types for LHS and RHS. +// --- u32.carbon -// --- arith_shift.carbon +library "[[@TEST_NAME]]"; -// TODO: Also test unsigned / logical right shift. +class Expect(N:! u32) {} +fn Test(N:! u32) -> Expect(N) { return {}; } -package ArithShift; +fn RightShift(a: u32, b: i32) -> u32 = "int.right_shift"; -fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; -fn Negate(a: i32) -> i32 = "int.snegate"; +fn F() { + Test(RightShift(0, 31)) as Expect(0); + Test(RightShift(1, 31)) as Expect(0); + Test(RightShift(1, 0)) as Expect(1); + Test(RightShift(1, 2)) as Expect(0); + Test(RightShift(22, 2)) as Expect(5); + Test(RightShift(0xFFFF_FFFF, 1)) as Expect(0x7FFF_FFFF); + Test(RightShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF); +} -// -1 >> 1 is -1. -var arr1: [i32; Negate(RightShift(Negate(1), 1))]; -let arr1_p: [i32; 1]* = &arr1; +fn RuntimeCall(a: u32, b: i32) -> u32 { + return RightShift(a, b); +} -// -10 >> 2 is -3. -var arr2: [i32; Negate(RightShift(Negate(10), 2))]; -let arr2_p: [i32; 3]* = &arr2; +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn RightShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.right_shift"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(RightShift(0, 31)) as Expect(0); + Test(RightShift(1, 31)) as Expect(0); + Test(RightShift(1, 0)) as Expect(1); + Test(RightShift(1, 2)) as Expect(0); + Test(RightShift(22, 2)) as Expect(5); + Test(RightShift(-1, 1)) as Expect(-1); + Test(RightShift(-2, 1)) as Expect(-1); + Test(RightShift(-10, 2)) as Expect(-3); + Test(RightShift(0xFFFF_FFFF, 1)) as Expect(0x7FFF_FFFF); + Test(RightShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF); + + Test(RightShift(0x1234_5678, 1_000_000_000)) as Expect(0); + Test(RightShift(-0x1234_5678, 1_000_000_000)) as Expect(-1); + Test(RightShift(0xFFFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(0); + Test(RightShift(0x7FFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(0); + Test(RightShift(-0x7FFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(-1); + Test(RightShift(-0x8000_0000_0000_0000, 1_000_000_000)) as Expect(-1); +} // --- fail_bad_shift.carbon -package BadShift; +library "[[@TEST_NAME]]"; fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; -fn Negate(a: i32) -> i32 = "int.snegate"; +fn RightShiftLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.right_shift"; -// Shift greater than size is disallowed. +// Shift greater than size is disallowed for sized types. let size_1: i32 = RightShift(1, 31); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance not in range [0, 32) in 1 >> 32 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 >> 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_2: i32 = RightShift(1, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_2: i32 = RightShift(1, 32); -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance not in range [0, 32) in 1 >> 33 [CompileTimeShiftOutOfRange] +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 >> 33` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_3: i32 = RightShift(1, 33); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_3: i32 = RightShift(1, 33); -// Negative shifts aren't allowed either. -// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+3]]:21: error: shift distance not in range [0, 32) in 1 >> -1 [CompileTimeShiftOutOfRange] -// CHECK:STDERR: let negative: i32 = RightShift(1, Negate(1)); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ -let negative: i32 = RightShift(1, Negate(1)); +// Negative shifts aren't allowed either, even for literals, even if the lhs is zero. +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:21: error: shift distance >= type width of 32 in `1 >> -1` [CompileTimeShiftOutOfRange] +// CHECK:STDERR: let negative: i32 = RightShift(1, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative: i32 = RightShift(1, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 >> -1` [CompileTimeShiftOutOfRange] +// CHECK:STDERR: let negative_zero: i32 = RightShift(0, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative_zero: i32 = RightShift(0, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:39: error: shift distance negative in `1 >> -1` [CompileTimeShiftNegative] +// CHECK:STDERR: let negative_lit: Core.IntLiteral() = RightShiftLit(1, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let negative_lit: Core.IntLiteral() = RightShiftLit(1, -1); +// CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+3]]:44: error: shift distance negative in `0 >> -1` [CompileTimeShiftNegative] +// CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = RightShiftLit(0, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +let negative_lit_zero: Core.IntLiteral() = RightShiftLit(0, -1); diff --git a/toolchain/check/testdata/builtins/int/sadd.carbon b/toolchain/check/testdata/builtins/int/sadd.carbon index 92a7a741edf4a..794ec245313a0 100644 --- a/toolchain/check/testdata/builtins/int/sadd.carbon +++ b/toolchain/check/testdata/builtins/int/sadd.carbon @@ -10,17 +10,47 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sadd.carbon -// --- int_add.carbon +// --- i32.carbon + +library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.sadd"; -var arr: [i32; Add(1, 2)]; -let arr_p: [i32; 3]* = &arr; +class Expect(N:! i32) {} +fn Test(N:! i32) -> Expect(N) { return {}; } + +fn F() { + Test(Add(0, 0)) as Expect(0); + Test(Add(1, 2)) as Expect(3); + Test(Add(0x7FFF_FFFE, 1)) as Expect(0x7FFF_FFFF); +} fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return Add(a, b); } +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn Add(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(Add(0, 0)) as Expect(0); + Test(Add(1, 2)) as Expect(3); + + // Test some cases that might -- but shouldn't -- overflow. + Test(Add(0x7FFF_FFFE, 1)) as Expect(0x7FFF_FFFF); + Test(Add(0x7FFF_FFFF, 1)) as Expect(0x8000_0000); + Test(Add(0x7FFF_FFFF_FFFF_FFFF, 1)) as Expect(0x8000_0000_0000_0000); + Test(Add(0xFFFF_FFFF_FFFF_FFFF, 1)) as Expect(0x1_0000_0000_0000_0000); + Test(Add(-0x8000_0000_0000_0000, -1)) as Expect(-0x8000_0000_0000_0001); + Test(Add(-0x8000_0000_0000_0000, -0x8000_0000_0000_0000)) as Expect(-0x1_0000_0000_0000_0000); +} + // --- fail_bad_decl.carbon package FailBadDecl; @@ -42,6 +72,28 @@ fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.sadd"; fn BadReturnType(a: i32, b: i32) -> bool = "int.sadd"; fn JustRight(a: i32, b: i32) -> i32 = "int.sadd"; +// Heterogeneous "add" is not supported. +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAdd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAdd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.sadd"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAdd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAdd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.sadd"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAdd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAdd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] +// CHECK:STDERR: fn MixedAdd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn MixedAdd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.sadd"; + // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:20: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_few: [i32; TooFew(1)]; // CHECK:STDERR: ^~~~~~~~~ @@ -61,7 +113,7 @@ var bad_return_type: [i32; BadReturnType(1, 2)]; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+7]]:21: error: 3 arguments passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: var bad_call: [i32; JustRight(1, 2, 3)]; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-21]]:1: note: calling function declared here [InCallToEntity] +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-43]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn JustRight(a: i32, b: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -86,7 +138,7 @@ package FailOverflow; fn Add(a: i32, b: i32) -> i32 = "int.sadd"; let a: i32 = Add(0x7FFFFFFF, 0); -// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in calculation 2147483647 + 1 [CompileTimeIntegerOverflow] +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in calculation `2147483647 + 1` [CompileTimeIntegerOverflow] // CHECK:STDERR: let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ let b: i32 = Add(0x7FFFFFFF, 1); diff --git a/toolchain/check/testdata/builtins/int/sdiv.carbon b/toolchain/check/testdata/builtins/int/sdiv.carbon index abec5e0bce168..f7c87e61ae4a0 100644 --- a/toolchain/check/testdata/builtins/int/sdiv.carbon +++ b/toolchain/check/testdata/builtins/int/sdiv.carbon @@ -30,23 +30,38 @@ fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; fn Negate(a: i32) -> i32 = "int.snegate"; // -0x7FFF_FFFF / -1 is OK. -let a: i32 = Div(Negate(0x7FFF_FFFF), Negate(1)); +let a: i32 = Div(-0x7FFF_FFFF, -1); // -0x8000_0000 / 1 is OK. -let b: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), 1); +let b: i32 = Div(-0x8000_0000, 1); // -0x8000_0000 / -1 overflows. -// CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation -2147483648 / -1 [CompileTimeIntegerOverflow] -// CHECK:STDERR: let c: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `-2147483648 / -1` [CompileTimeIntegerOverflow] +// CHECK:STDERR: let c: i32 = Div(-0x8000_0000, -1); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -let c: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); +let c: i32 = Div(-0x8000_0000, -1); + +// --- literal_no_overflow.carbon + +library "[[@TEST_NAME]]"; + +fn Div(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sdiv"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(Div(-0x8000_0000, -1)) as Expect(0x8000_0000); + Test(Div(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); +} // --- fail_div_by_zero.carbon package FailDivByZero; fn Div(a: i32, b: i32) -> i32 = "int.sdiv"; +fn DivLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sdiv"; // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let a: i32 = Div(1, 0); @@ -54,7 +69,20 @@ fn Div(a: i32, b: i32) -> i32 = "int.sdiv"; // CHECK:STDERR: let a: i32 = Div(1, 0); -// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+3]]:14: error: division by zero [CompileTimeDivisionByZero] +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let b: i32 = Div(0, 0); // CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: let b: i32 = Div(0, 0); + +// IntLiteral allows "overflow" by widening its representation, but not overflow to infinity. +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:28: error: division by zero [CompileTimeDivisionByZero] +// CHECK:STDERR: let c: Core.IntLiteral() = DivLit(1, 0); +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +let c: Core.IntLiteral() = DivLit(1, 0); + +// CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+3]]:28: error: division by zero [CompileTimeDivisionByZero] +// CHECK:STDERR: let d: Core.IntLiteral() = DivLit(0, 0); +// CHECK:STDERR: ^~~~~~~~~~~~ +let d: Core.IntLiteral() = DivLit(0, 0); diff --git a/toolchain/check/testdata/builtins/int/smod.carbon b/toolchain/check/testdata/builtins/int/smod.carbon index 72f5f4f01cd1b..dbcd590aa4017 100644 --- a/toolchain/check/testdata/builtins/int/smod.carbon +++ b/toolchain/check/testdata/builtins/int/smod.carbon @@ -37,7 +37,7 @@ 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 [CompileTimeIntegerOverflow] +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `-2147483648 % -1` [CompileTimeIntegerOverflow] // CHECK:STDERR: let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/builtins/int/smul.carbon b/toolchain/check/testdata/builtins/int/smul.carbon index 563d39d864353..403efe866b399 100644 --- a/toolchain/check/testdata/builtins/int/smul.carbon +++ b/toolchain/check/testdata/builtins/int/smul.carbon @@ -10,17 +10,55 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smul.carbon -// --- int_mul.carbon +// --- i32.carbon + +library "[[@TEST_NAME]]"; fn Mul(a: i32, b: i32) -> i32 = "int.smul"; -var arr: [i32; Mul(3, 2)]; -let arr_p: [i32; 6]* = &arr; +class Expect(N:! i32) {} +fn Test(N:! i32) -> Expect(N) { return {}; } + +fn F() { + Test(Mul(0, 0)) as Expect(0); + Test(Mul(0, 3)) as Expect(0); + Test(Mul(1, 2)) as Expect(2); + Test(Mul(3, 2)) as Expect(6); + Test(Mul(0x7FFF_FFFF, 1)) as Expect(0x7FFF_FFFF); + Test(Mul(0x7FFF_FFFF, -1)) as Expect(-0x7FFF_FFFF); +} fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return Mul(a, b); } +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn Mul(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.smul"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(Mul(0, 0)) as Expect(0); + Test(Mul(0, 3)) as Expect(0); + Test(Mul(1, 2)) as Expect(2); + Test(Mul(3, 2)) as Expect(6); + Test(Mul(0x7FFF_FFFF, 1)) as Expect(0x7FFF_FFFF); + Test(Mul(0x7FFF_FFFF, -1)) as Expect(-0x7FFF_FFFF); + + // Test some cases that might -- but shouldn't -- overflow. + Test(Mul(0x8000_0000_0000_0000, 1)) as Expect(0x8000_0000_0000_0000); + Test(Mul(0x8000_0000_0000_0000, -1)) as Expect(-0x8000_0000_0000_0000); + Test(Mul(-0x8000_0000_0000_0000, 1)) as Expect(-0x8000_0000_0000_0000); + Test(Mul(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); + Test(Mul(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); + Test(Mul(-0x8000_0000_0000_0000, -0x8000_0000_0000_0000)) + as Expect(0x4000_0000_0000_0000_0000_0000_0000_0000); +} + // --- fail_overflow.carbon package FailOverflow; @@ -28,7 +66,7 @@ package FailOverflow; fn Mul(a: i32, b: i32) -> i32 = "int.smul"; let a: i32 = Mul(0x7FFF, 0x10000); -// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in calculation 32768 * 65536 [CompileTimeIntegerOverflow] +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in calculation `32768 * 65536` [CompileTimeIntegerOverflow] // CHECK:STDERR: let b: i32 = Mul(0x8000, 0x10000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ let b: i32 = Mul(0x8000, 0x10000); diff --git a/toolchain/check/testdata/builtins/int/snegate.carbon b/toolchain/check/testdata/builtins/int/snegate.carbon index a0c03f880e7c1..93b3d41d66a3e 100644 --- a/toolchain/check/testdata/builtins/int/snegate.carbon +++ b/toolchain/check/testdata/builtins/int/snegate.carbon @@ -23,6 +23,21 @@ fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { return Negate(a); } +// --- literal.carbon + +library "[[@TEST_NAME]]"; + +fn Negate(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.snegate"; + +class Expect(N:! Core.IntLiteral()) {} +fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } + +fn F() { + Test(Negate(0)) as Expect(0); + Test(Negate(1)) as Expect(0 - 1); + Test(Negate(0 - 0x8000_0000_0000_0000)) as Expect(0x8000_0000_0000_0000); +} + // --- fail_bad_decl.carbon package FailBadDecl; @@ -110,10 +125,10 @@ fn Negate(a: i32) -> i32 = "int.snegate"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; // -(-INT_MAX) is INT_MAX. -let a: i32 = Negate(Negate(0x7FFFFFFF)); +let a: i32 = Negate(Negate(0x7FFF_FFFF)); -// -(-INT_MAX - 1) is too large for i32. +// -INT_MIN is too large for i32. // CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in negation of -2147483648 [CompileTimeIntegerNegateOverflow] -// CHECK:STDERR: let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1)); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -let b: i32 = Negate(Sub(Negate(0x7FFFFFFF), 1)); +// CHECK:STDERR: let b: i32 = Negate(-0x8000_0000); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +let b: i32 = Negate(-0x8000_0000); diff --git a/toolchain/check/testdata/builtins/int/ssub.carbon b/toolchain/check/testdata/builtins/int/ssub.carbon index c27bfe561aac8..a7cdfe90ca12b 100644 --- a/toolchain/check/testdata/builtins/int/ssub.carbon +++ b/toolchain/check/testdata/builtins/int/ssub.carbon @@ -29,7 +29,7 @@ fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; 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 [CompileTimeIntegerOverflow] +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: error: integer overflow in calculation `-2147483647 - 2` [CompileTimeIntegerOverflow] // CHECK:STDERR: let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); diff --git a/toolchain/check/testdata/builtins/print/char.carbon b/toolchain/check/testdata/builtins/print/char.carbon index a6a1a1b8f9501..79d3d4ae90951 100644 --- a/toolchain/check/testdata/builtins/print/char.carbon +++ b/toolchain/check/testdata/builtins/print/char.carbon @@ -46,12 +46,12 @@ fn Main() { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .PrintChar = %import_ref.193 +// CHECK:STDOUT: .PrintChar = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.193: %PrintChar.type.2 = import_ref Core//io, PrintChar, loaded [template = constants.%PrintChar.2] +// CHECK:STDOUT: %import_ref.229: %PrintChar.type.2 = import_ref Core//io, PrintChar, loaded [template = constants.%PrintChar.2] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -95,7 +95,7 @@ fn Main() { // CHECK:STDOUT: %.loc16_13.2: %i32 = converted %int_1, %.loc16_13.1 [template = constants.%int_1.2] // CHECK:STDOUT: %print.char.loc16: init %i32 = call %PrintChar.ref.loc16(%.loc16_13.2) // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] -// CHECK:STDOUT: %PrintChar.ref.loc17: %PrintChar.type.2 = name_ref PrintChar, imports.%import_ref.193 [template = constants.%PrintChar.2] +// CHECK:STDOUT: %PrintChar.ref.loc17: %PrintChar.type.2 = name_ref PrintChar, imports.%import_ref.229 [template = constants.%PrintChar.2] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [template = constants.%int_2.1] // CHECK:STDOUT: %impl.elem0.loc17: %Convert.type.2 = interface_witness_access constants.%interface.19, element0 [template = constants.%Convert.10] // CHECK:STDOUT: %Convert.bound.loc17: = bound_method %int_2, %impl.elem0.loc17 [template = constants.%Convert.bound.2] diff --git a/toolchain/check/testdata/builtins/print/int.carbon b/toolchain/check/testdata/builtins/print/int.carbon index 3725d6cc5f586..d114966529d62 100644 --- a/toolchain/check/testdata/builtins/print/int.carbon +++ b/toolchain/check/testdata/builtins/print/int.carbon @@ -48,12 +48,12 @@ fn Main() { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Print = %import_ref.193 +// CHECK:STDOUT: .Print = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.193: %Print.type.2 = import_ref Core//io, Print, loaded [template = constants.%Print.2] +// CHECK:STDOUT: %import_ref.229: %Print.type.2 = import_ref Core//io, Print, loaded [template = constants.%Print.2] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -91,7 +91,7 @@ fn Main() { // CHECK:STDOUT: %.loc16_9.2: %i32 = converted %int_1, %.loc16_9.1 [template = constants.%int_1.2] // CHECK:STDOUT: %print.int.loc16: init %empty_tuple.type = call %Print.ref.loc16(%.loc16_9.2) // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [template = imports.%Core] -// CHECK:STDOUT: %Print.ref.loc18: %Print.type.2 = name_ref Print, imports.%import_ref.193 [template = constants.%Print.2] +// CHECK:STDOUT: %Print.ref.loc18: %Print.type.2 = name_ref Print, imports.%import_ref.229 [template = constants.%Print.2] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [template = constants.%int_2.1] // CHECK:STDOUT: %impl.elem0.loc18: %Convert.type.2 = interface_witness_access constants.%interface.19, element0 [template = constants.%Convert.10] // CHECK:STDOUT: %Convert.bound.loc18: = bound_method %int_2, %impl.elem0.loc18 [template = constants.%Convert.bound.2] diff --git a/toolchain/check/testdata/class/generic/import.carbon b/toolchain/check/testdata/class/generic/import.carbon index e180b5a83d385..3e773962f29eb 100644 --- a/toolchain/check/testdata/class/generic/import.carbon +++ b/toolchain/check/testdata/class/generic/import.carbon @@ -282,15 +282,15 @@ class Class(U:! type) { // CHECK:STDOUT: imports { // CHECK:STDOUT: %import_ref.2: %CompleteClass.type = import_ref Main//foo, CompleteClass, loaded [template = constants.%CompleteClass.generic] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Int = %import_ref.197 -// CHECK:STDOUT: .ImplicitAs = %import_ref.198 +// CHECK:STDOUT: .Int = %import_ref.233 +// CHECK:STDOUT: .ImplicitAs = %import_ref.234 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.193: = import_ref Main//foo, loc9_1, loaded [template = constants.%complete_type.4] -// CHECK:STDOUT: %import_ref.194 = import_ref Main//foo, inst37 [no loc], unloaded -// CHECK:STDOUT: %import_ref.195 = import_ref Main//foo, loc7_8, unloaded -// CHECK:STDOUT: %import_ref.196 = import_ref Main//foo, loc8_17, unloaded +// CHECK:STDOUT: %import_ref.229: = import_ref Main//foo, loc9_1, loaded [template = constants.%complete_type.4] +// CHECK:STDOUT: %import_ref.230 = import_ref Main//foo, inst37 [no loc], unloaded +// CHECK:STDOUT: %import_ref.231 = import_ref Main//foo, loc7_8, unloaded +// CHECK:STDOUT: %import_ref.232 = import_ref Main//foo, loc8_17, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -357,10 +357,10 @@ class Class(U:! type) { // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.194 -// CHECK:STDOUT: .n = imports.%import_ref.195 -// CHECK:STDOUT: .F = imports.%import_ref.196 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.193 +// CHECK:STDOUT: .Self = imports.%import_ref.230 +// CHECK:STDOUT: .n = imports.%import_ref.231 +// CHECK:STDOUT: .F = imports.%import_ref.232 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.229 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/import.carbon b/toolchain/check/testdata/class/import.carbon index acbd0a629481f..4c9dc7b152020 100644 --- a/toolchain/check/testdata/class/import.carbon +++ b/toolchain/check/testdata/class/import.carbon @@ -204,14 +204,14 @@ fn Run() { // CHECK:STDOUT: %import_ref.10: = import_ref Main//a, loc9_1, loaded [template = constants.%complete_type.3] // CHECK:STDOUT: %import_ref.11 = import_ref Main//a, inst21 [no loc], unloaded // CHECK:STDOUT: %import_ref.12: %Field.elem = import_ref Main//a, loc8_8, loaded [template = %.1] -// CHECK:STDOUT: %import_ref.201: = import_ref Main//a, loc16_1, loaded [template = constants.%complete_type.1] -// CHECK:STDOUT: %import_ref.202 = import_ref Main//a, inst56 [no loc], unloaded -// CHECK:STDOUT: %import_ref.203: %F.type = import_ref Main//a, loc14_21, loaded [template = constants.%F] -// CHECK:STDOUT: %import_ref.204: %G.type = import_ref Main//a, loc15_27, loaded [template = constants.%G] -// CHECK:STDOUT: %import_ref.205: = import_ref Main//a, loc16_1, loaded [template = constants.%complete_type.1] -// CHECK:STDOUT: %import_ref.206 = import_ref Main//a, inst56 [no loc], unloaded -// CHECK:STDOUT: %import_ref.207 = import_ref Main//a, loc14_21, unloaded -// CHECK:STDOUT: %import_ref.208 = import_ref Main//a, loc15_27, unloaded +// CHECK:STDOUT: %import_ref.237: = import_ref Main//a, loc16_1, loaded [template = constants.%complete_type.1] +// CHECK:STDOUT: %import_ref.238 = import_ref Main//a, inst56 [no loc], unloaded +// CHECK:STDOUT: %import_ref.239: %F.type = import_ref Main//a, loc14_21, loaded [template = constants.%F] +// CHECK:STDOUT: %import_ref.240: %G.type = import_ref Main//a, loc15_27, loaded [template = constants.%G] +// CHECK:STDOUT: %import_ref.241: = import_ref Main//a, loc16_1, loaded [template = constants.%complete_type.1] +// CHECK:STDOUT: %import_ref.242 = import_ref Main//a, inst56 [no loc], unloaded +// CHECK:STDOUT: %import_ref.243 = import_ref Main//a, loc14_21, unloaded +// CHECK:STDOUT: %import_ref.244 = import_ref Main//a, loc15_27, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -243,18 +243,18 @@ fn Run() { // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDeclared.1 [from "a.carbon"] { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.202 -// CHECK:STDOUT: .F = imports.%import_ref.203 -// CHECK:STDOUT: .G = imports.%import_ref.204 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.201 +// CHECK:STDOUT: .Self = imports.%import_ref.238 +// CHECK:STDOUT: .F = imports.%import_ref.239 +// CHECK:STDOUT: .G = imports.%import_ref.240 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.237 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDeclared.2 [from "a.carbon"] { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.206 -// CHECK:STDOUT: .F = imports.%import_ref.207 -// CHECK:STDOUT: .G = imports.%import_ref.208 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.205 +// CHECK:STDOUT: .Self = imports.%import_ref.242 +// CHECK:STDOUT: .F = imports.%import_ref.243 +// CHECK:STDOUT: .G = imports.%import_ref.244 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.241 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Incomplete [from "a.carbon"]; @@ -298,12 +298,12 @@ fn Run() { // CHECK:STDOUT: %.loc12_30: init %ForwardDeclared.1 = converted %.loc12_29.1, %.loc12_29.2 [template = constants.%ForwardDeclared.val] // CHECK:STDOUT: assign %c.var, %.loc12_30 // CHECK:STDOUT: %c.ref.loc13: ref %ForwardDeclared.1 = name_ref c, %c -// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%import_ref.203 [template = constants.%F] +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%import_ref.239 [template = constants.%F] // CHECK:STDOUT: %F.bound: = bound_method %c.ref.loc13, %F.ref // CHECK:STDOUT: %.loc13: %ForwardDeclared.1 = bind_value %c.ref.loc13 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.bound(%.loc13) // CHECK:STDOUT: %c.ref.loc14: ref %ForwardDeclared.1 = name_ref c, %c -// CHECK:STDOUT: %G.ref: %G.type = name_ref G, imports.%import_ref.204 [template = constants.%G] +// CHECK:STDOUT: %G.ref: %G.type = name_ref G, imports.%import_ref.240 [template = constants.%G] // CHECK:STDOUT: %G.bound: = bound_method %c.ref.loc14, %G.ref // CHECK:STDOUT: %addr.loc14: %ptr.3 = addr_of %c.ref.loc14 // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.bound(%addr.loc14) @@ -319,5 +319,5 @@ fn Run() { // CHECK:STDOUT: // CHECK:STDOUT: fn @F[%self.param_patt: %ForwardDeclared.1]() [from "a.carbon"]; // CHECK:STDOUT: -// CHECK:STDOUT: fn @G[addr .inst942: %ptr.3]() [from "a.carbon"]; +// CHECK:STDOUT: fn @G[addr .inst990: %ptr.3]() [from "a.carbon"]; // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/method.carbon b/toolchain/check/testdata/function/builtin/method.carbon index 9b5435af48154..837849b12aade 100644 --- a/toolchain/check/testdata/function/builtin/method.carbon +++ b/toolchain/check/testdata/function/builtin/method.carbon @@ -42,7 +42,7 @@ var arr: [i32; (1 as i32).(I.F)(2)]; // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .As = %import_ref.5 -// CHECK:STDOUT: .ImplicitAs = %import_ref.193 +// CHECK:STDOUT: .ImplicitAs = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/if_expr/control_flow.carbon b/toolchain/check/testdata/if_expr/control_flow.carbon index 908bd65f8232d..704a40a6f9f22 100644 --- a/toolchain/check/testdata/if_expr/control_flow.carbon +++ b/toolchain/check/testdata/if_expr/control_flow.carbon @@ -46,7 +46,7 @@ fn F(b: bool) -> i32 { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Bool = %import_ref.193 +// CHECK:STDOUT: .Bool = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon index b3a19ff0101fa..6d2dcd0d36892 100644 --- a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon +++ b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon @@ -67,7 +67,7 @@ class C { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Float = %import_ref.193 +// CHECK:STDOUT: .Float = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/index/fail_negative_indexing.carbon b/toolchain/check/testdata/index/fail_negative_indexing.carbon index 9e3d86e8803cb..b6dcd848bb138 100644 --- a/toolchain/check/testdata/index/fail_negative_indexing.carbon +++ b/toolchain/check/testdata/index/fail_negative_indexing.carbon @@ -9,7 +9,7 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/index/fail_negative_indexing.carbon var c: [i32; 2] = (42, 42); -// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:16: error: cannot access member of interface `Core.Negate` in type `Core.IntLiteral` that does not implement that interface [MissingImplInMemberAccess] +// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:16: error: array index `-10` is past the end of type `[i32; 2]` [ArrayIndexOutOfBounds] // CHECK:STDERR: var d: i32 = c[-10]; // CHECK:STDERR: ^~~ var d: i32 = c[-10]; @@ -29,19 +29,28 @@ var d: i32 = c[-10]; // CHECK:STDOUT: %Convert.type.10: type = fn_type @Convert.2, @impl.1(%int_32) [template] // CHECK:STDOUT: %Convert.10: %Convert.type.10 = struct_value () [template] // CHECK:STDOUT: %interface.19: = interface_witness (%Convert.10) [template] -// CHECK:STDOUT: %Convert.bound: = bound_method %int_42.1, %Convert.10 [template] -// CHECK:STDOUT: %Convert.specific_fn: = specific_function %Convert.bound, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %Convert.bound.1: = bound_method %int_42.1, %Convert.10 [template] +// CHECK:STDOUT: %Convert.specific_fn.1: = specific_function %Convert.bound.1, @Convert.2(%int_32) [template] // CHECK:STDOUT: %int_42.2: %i32 = int_value 42 [template] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [template] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_42.2, %int_42.2) [template] // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [template] +// CHECK:STDOUT: %Op.type.13: type = fn_type @Op.13 [template] +// CHECK:STDOUT: %Op.type.14: type = fn_type @Op.14 [template] +// CHECK:STDOUT: %Op.14: %Op.type.14 = struct_value () [template] +// CHECK:STDOUT: %interface.20: = interface_witness (%Op.14) [template] +// CHECK:STDOUT: %Op.bound: = bound_method %int_10, %Op.14 [template] +// CHECK:STDOUT: %int_-10.1: Core.IntLiteral = int_value -10 [template] +// CHECK:STDOUT: %Convert.bound.2: = bound_method %int_-10.1, %Convert.10 [template] +// CHECK:STDOUT: %Convert.specific_fn.2: = specific_function %Convert.bound.2, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %int_-10.2: %i32 = int_value -10 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Negate = %import_ref.193 +// CHECK:STDOUT: .Negate = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -66,16 +75,16 @@ var d: i32 = c[-10]; // CHECK:STDOUT: %int_42.loc11_24: Core.IntLiteral = int_value 42 [template = constants.%int_42.1] // CHECK:STDOUT: %.loc11_26.1: %tuple.type = tuple_literal (%int_42.loc11_20, %int_42.loc11_24) // CHECK:STDOUT: %impl.elem0.loc11_26.1: %Convert.type.2 = interface_witness_access constants.%interface.19, element0 [template = constants.%Convert.10] -// CHECK:STDOUT: %Convert.bound.loc11_26.1: = bound_method %int_42.loc11_20, %impl.elem0.loc11_26.1 [template = constants.%Convert.bound] -// CHECK:STDOUT: %Convert.specific_fn.loc11_26.1: = specific_function %Convert.bound.loc11_26.1, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn] +// CHECK:STDOUT: %Convert.bound.loc11_26.1: = bound_method %int_42.loc11_20, %impl.elem0.loc11_26.1 [template = constants.%Convert.bound.1] +// CHECK:STDOUT: %Convert.specific_fn.loc11_26.1: = specific_function %Convert.bound.loc11_26.1, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1] // CHECK:STDOUT: %int.convert_checked.loc11_26.1: init %i32 = call %Convert.specific_fn.loc11_26.1(%int_42.loc11_20) [template = constants.%int_42.2] // CHECK:STDOUT: %.loc11_26.2: init %i32 = converted %int_42.loc11_20, %int.convert_checked.loc11_26.1 [template = constants.%int_42.2] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [template = constants.%int_0] // CHECK:STDOUT: %.loc11_26.3: ref %i32 = array_index file.%c.var, %int_0 // CHECK:STDOUT: %.loc11_26.4: init %i32 = initialize_from %.loc11_26.2 to %.loc11_26.3 [template = constants.%int_42.2] // CHECK:STDOUT: %impl.elem0.loc11_26.2: %Convert.type.2 = interface_witness_access constants.%interface.19, element0 [template = constants.%Convert.10] -// CHECK:STDOUT: %Convert.bound.loc11_26.2: = bound_method %int_42.loc11_24, %impl.elem0.loc11_26.2 [template = constants.%Convert.bound] -// CHECK:STDOUT: %Convert.specific_fn.loc11_26.2: = specific_function %Convert.bound.loc11_26.2, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn] +// CHECK:STDOUT: %Convert.bound.loc11_26.2: = bound_method %int_42.loc11_24, %impl.elem0.loc11_26.2 [template = constants.%Convert.bound.1] +// CHECK:STDOUT: %Convert.specific_fn.loc11_26.2: = specific_function %Convert.bound.loc11_26.2, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.1] // CHECK:STDOUT: %int.convert_checked.loc11_26.2: init %i32 = call %Convert.specific_fn.loc11_26.2(%int_42.loc11_24) [template = constants.%int_42.2] // CHECK:STDOUT: %.loc11_26.5: init %i32 = converted %int_42.loc11_24, %int.convert_checked.loc11_26.2 [template = constants.%int_42.2] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [template = constants.%int_1] @@ -86,9 +95,20 @@ var d: i32 = c[-10]; // CHECK:STDOUT: assign file.%c.var, %.loc11_27 // CHECK:STDOUT: %c.ref: ref %array_type = name_ref c, file.%c // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [template = constants.%int_10] +// CHECK:STDOUT: %impl.elem0.loc15_16.1: %Op.type.13 = interface_witness_access constants.%interface.20, element0 [template = constants.%Op.14] +// CHECK:STDOUT: %Op.bound: = bound_method %int_10, %impl.elem0.loc15_16.1 [template = constants.%Op.bound] +// CHECK:STDOUT: %int.snegate: init Core.IntLiteral = call %Op.bound(%int_10) [template = constants.%int_-10.1] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32] -// CHECK:STDOUT: %.loc15_19.1: ref %i32 = array_index %c.ref, [template = ] +// CHECK:STDOUT: %impl.elem0.loc15_16.2: %Convert.type.2 = interface_witness_access constants.%interface.19, element0 [template = constants.%Convert.10] +// CHECK:STDOUT: %Convert.bound.loc15: = bound_method %int.snegate, %impl.elem0.loc15_16.2 [template = constants.%Convert.bound.2] +// CHECK:STDOUT: %Convert.specific_fn.loc15: = specific_function %Convert.bound.loc15, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn.2] +// CHECK:STDOUT: %.loc15_16.1: Core.IntLiteral = value_of_initializer %int.snegate [template = constants.%int_-10.1] +// CHECK:STDOUT: %.loc15_16.2: Core.IntLiteral = converted %int.snegate, %.loc15_16.1 [template = constants.%int_-10.1] +// CHECK:STDOUT: %int.convert_checked.loc15: init %i32 = call %Convert.specific_fn.loc15(%.loc15_16.2) [template = constants.%int_-10.2] +// CHECK:STDOUT: %.loc15_16.3: %i32 = value_of_initializer %int.convert_checked.loc15 [template = constants.%int_-10.2] +// CHECK:STDOUT: %.loc15_16.4: %i32 = converted %int.snegate, %.loc15_16.3 [template = constants.%int_-10.2] +// CHECK:STDOUT: %.loc15_19.1: ref %i32 = array_index %c.ref, %.loc15_16.4 [template = ] // CHECK:STDOUT: %.loc15_19.2: %i32 = bind_value %.loc15_19.1 // CHECK:STDOUT: assign file.%d.var, %.loc15_19.2 // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon b/toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon index 1c2b8af30c588..37bdaab74288d 100644 --- a/toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon @@ -11,10 +11,10 @@ fn Main() -> i32 { // The following line has two mismatches, but after the first, it shouldn't // keep erroring. - // CHECK:STDERR: fail_type_mismatch_once.carbon:[[@LINE+3]]:10: error: cannot access member of interface `Core.Add` in type `Core.IntLiteral` that does not implement that interface [MissingImplInMemberAccess] - // CHECK:STDERR: return 12 + 3.4 + 12; + // CHECK:STDERR: fail_type_mismatch_once.carbon:[[@LINE+3]]:10: error: cannot access member of interface `Core.Add` in type `{}` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: return {} + 3.4 + 12; // CHECK:STDERR: ^~~~~~~~ - return 12 + 3.4 + 12; + return {} + 3.4 + 12; } // CHECK:STDOUT: --- fail_type_mismatch_once.carbon @@ -24,9 +24,10 @@ fn Main() -> i32 { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] // CHECK:STDOUT: %Main.type: type = fn_type @Main [template] // CHECK:STDOUT: %Main: %Main.type = struct_value () [template] -// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] // CHECK:STDOUT: %float: f64 = float_literal 3.4000000000000004 [template] // CHECK:STDOUT: %Op.type: type = fn_type @Op [template] +// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -57,9 +58,9 @@ fn Main() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() -> %i32 { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %int_12.loc17_10: Core.IntLiteral = int_value 12 [template = constants.%int_12] +// CHECK:STDOUT: %.loc17: %empty_struct_type = struct_literal () // CHECK:STDOUT: %float: f64 = float_literal 3.4000000000000004 [template = constants.%float] -// CHECK:STDOUT: %int_12.loc17_21: Core.IntLiteral = int_value 12 [template = constants.%int_12] +// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [template = constants.%int_12] // CHECK:STDOUT: %impl.elem0: %Op.type = interface_witness_access , element0 [template = ] // CHECK:STDOUT: %Op.bound: = bound_method , %impl.elem0 [template = ] // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon b/toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon index b67d0fa9bf4c1..4b7c3eb7e1938 100644 --- a/toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon @@ -9,10 +9,10 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon fn Main() -> i32 { - // CHECK:STDERR: fail_unimplemented_op.carbon:[[@LINE+3]]:10: error: cannot access member of interface `Core.Add` in type `Core.IntLiteral` that does not implement that interface [MissingImplInMemberAccess] - // CHECK:STDERR: return 12 + 34; + // CHECK:STDERR: fail_unimplemented_op.carbon:[[@LINE+3]]:10: error: cannot access member of interface `Core.Add` in type `{}` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: return {} + 34; // CHECK:STDERR: ^~~~~~~ - return 12 + 34; + return {} + 34; } // CHECK:STDOUT: --- fail_unimplemented_op.carbon @@ -22,7 +22,7 @@ fn Main() -> i32 { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] // CHECK:STDOUT: %Main.type: type = fn_type @Main [template] // CHECK:STDOUT: %Main: %Main.type = struct_value () [template] -// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] // CHECK:STDOUT: %int_34: Core.IntLiteral = int_value 34 [template] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -54,7 +54,7 @@ fn Main() -> i32 { // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() -> %i32 { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [template = constants.%int_12] +// CHECK:STDOUT: %.loc15: %empty_struct_type = struct_literal () // CHECK:STDOUT: %int_34: Core.IntLiteral = int_value 34 [template = constants.%int_34] // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/overloaded/bit_complement.carbon b/toolchain/check/testdata/operators/overloaded/bit_complement.carbon index cbacbb9dcdd89..cb1d942f31620 100644 --- a/toolchain/check/testdata/operators/overloaded/bit_complement.carbon +++ b/toolchain/check/testdata/operators/overloaded/bit_complement.carbon @@ -57,7 +57,7 @@ fn TestOp(a: C) -> C { // 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: %BitComplement.ref: type = name_ref BitComplement, imports.%import_ref.1 [template = constants.%BitComplement.type] @@ -77,7 +77,7 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %BitComplement.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %BitComplement.ref { // CHECK:STDOUT: %Op.decl: %Op.type.1 = fn_decl @Op.1 [template = constants.%Op.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/dec.carbon b/toolchain/check/testdata/operators/overloaded/dec.carbon index 48c67c69529a9..036345b6a5e76 100644 --- a/toolchain/check/testdata/operators/overloaded/dec.carbon +++ b/toolchain/check/testdata/operators/overloaded/dec.carbon @@ -58,7 +58,7 @@ fn TestOp() { // 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: %Dec.ref: type = name_ref Dec, imports.%import_ref.1 [template = constants.%Dec.type] @@ -66,7 +66,7 @@ fn TestOp() { // CHECK:STDOUT: %TestOp.decl: %TestOp.type = fn_decl @TestOp [template = constants.%TestOp] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Dec.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Dec.ref { // CHECK:STDOUT: %Op.decl: %Op.type.1 = fn_decl @Op.1 [template = constants.%Op.1] { // CHECK:STDOUT: %self.patt: %ptr.1 = binding_pattern self // CHECK:STDOUT: %self.param_patt: %ptr.1 = value_param_pattern %self.patt, runtime_param0 diff --git a/toolchain/check/testdata/operators/overloaded/eq.carbon b/toolchain/check/testdata/operators/overloaded/eq.carbon index bc531ae1a361b..0a6cbd1a8a9b5 100644 --- a/toolchain/check/testdata/operators/overloaded/eq.carbon +++ b/toolchain/check/testdata/operators/overloaded/eq.carbon @@ -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.12 +// CHECK:STDOUT: .ImplicitAs = %import_ref.27 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/overloaded/fail_error_recovery.carbon b/toolchain/check/testdata/operators/overloaded/fail_error_recovery.carbon index 3cfe71e01f63e..7caf31184ebc3 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_error_recovery.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_error_recovery.carbon @@ -37,7 +37,7 @@ fn G(n: i32) { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] // CHECK:STDOUT: %G.type: type = fn_type @G [template] // CHECK:STDOUT: %G: %G.type = struct_value () [template] -// CHECK:STDOUT: %Op.type.14: type = fn_type @Op.2, @impl.7(%int_32) [template] +// CHECK:STDOUT: %Op.type.14: type = fn_type @Op.2, @impl.13(%int_32) [template] // CHECK:STDOUT: %Op.14: %Op.type.14 = struct_value () [template] // CHECK:STDOUT: %interface.19: = interface_witness (%Op.14) [template] // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon b/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon index 361503889b22b..4828300334357 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon @@ -59,9 +59,9 @@ fn TestRef(b: C) { // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Negate = %import_ref.1 -// CHECK:STDOUT: .Add = %import_ref.6 -// CHECK:STDOUT: .AddAssign = %import_ref.11 -// CHECK:STDOUT: .Inc = %import_ref.16 +// CHECK:STDOUT: .Add = %import_ref.39 +// CHECK:STDOUT: .AddAssign = %import_ref.41 +// CHECK:STDOUT: .Inc = %import_ref.46 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon index dc9336622e968..e3fabdff6aeae 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon @@ -78,7 +78,7 @@ fn TestAssign(b: D) { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Add = %import_ref.1 // CHECK:STDOUT: .AddAssign = %import_ref.5 -// CHECK:STDOUT: .ImplicitAs = %import_ref.10 +// CHECK:STDOUT: .ImplicitAs = %import_ref.43 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/operators/overloaded/inc.carbon b/toolchain/check/testdata/operators/overloaded/inc.carbon index 20892ea4a5ddd..87045188460c8 100644 --- a/toolchain/check/testdata/operators/overloaded/inc.carbon +++ b/toolchain/check/testdata/operators/overloaded/inc.carbon @@ -58,7 +58,7 @@ fn TestOp() { // 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: %Inc.ref: type = name_ref Inc, imports.%import_ref.1 [template = constants.%Inc.type] @@ -66,7 +66,7 @@ fn TestOp() { // CHECK:STDOUT: %TestOp.decl: %TestOp.type = fn_decl @TestOp [template = constants.%TestOp] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Inc.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Inc.ref { // CHECK:STDOUT: %Op.decl: %Op.type.1 = fn_decl @Op.1 [template = constants.%Op.1] { // CHECK:STDOUT: %self.patt: %ptr.1 = binding_pattern self // CHECK:STDOUT: %self.param_patt: %ptr.1 = value_param_pattern %self.patt, runtime_param0 diff --git a/toolchain/check/testdata/operators/overloaded/negate.carbon b/toolchain/check/testdata/operators/overloaded/negate.carbon index 907f48a5bae64..532c6cf0faa00 100644 --- a/toolchain/check/testdata/operators/overloaded/negate.carbon +++ b/toolchain/check/testdata/operators/overloaded/negate.carbon @@ -57,7 +57,7 @@ fn TestOp(a: C) -> C { // 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: %Negate.ref: type = name_ref Negate, imports.%import_ref.1 [template = constants.%Negate.type] @@ -77,7 +77,7 @@ fn TestOp(a: C) -> C { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: %C.ref as %Negate.ref { +// CHECK:STDOUT: impl @impl.1: %C.ref as %Negate.ref { // CHECK:STDOUT: %Op.decl: %Op.type.1 = fn_decl @Op.1 [template = constants.%Op.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/packages/implicit_imports_prelude.carbon b/toolchain/check/testdata/packages/implicit_imports_prelude.carbon index cf73aceba5fcc..1b170c1d4c4ca 100644 --- a/toolchain/check/testdata/packages/implicit_imports_prelude.carbon +++ b/toolchain/check/testdata/packages/implicit_imports_prelude.carbon @@ -78,7 +78,7 @@ var b: i32 = a; // CHECK:STDOUT: imports { // CHECK:STDOUT: %import_ref.1: ref %i32 = import_ref Main//lib, a, loaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Int = %import_ref.191 +// CHECK:STDOUT: .Int = %import_ref.227 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/pointer/import.carbon b/toolchain/check/testdata/pointer/import.carbon index c5906b3482eb7..6818ae8c6d3cb 100644 --- a/toolchain/check/testdata/pointer/import.carbon +++ b/toolchain/check/testdata/pointer/import.carbon @@ -86,7 +86,7 @@ var a: i32* = a_ref; // CHECK:STDOUT: %import_ref.1 = import_ref Implicit//default, a_orig, unloaded // CHECK:STDOUT: %import_ref.2: ref %ptr = import_ref Implicit//default, a_ref, loaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Int = %import_ref.192 +// CHECK:STDOUT: .Int = %import_ref.228 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/return/returned_var_scope.carbon b/toolchain/check/testdata/return/returned_var_scope.carbon index 1038f900fb4ef..1595999ccc5fc 100644 --- a/toolchain/check/testdata/return/returned_var_scope.carbon +++ b/toolchain/check/testdata/return/returned_var_scope.carbon @@ -57,7 +57,7 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Bool = %import_ref.193 +// CHECK:STDOUT: .Bool = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/struct/import.carbon b/toolchain/check/testdata/struct/import.carbon index 7d607e20dfb61..c65e6c5380aed 100644 --- a/toolchain/check/testdata/struct/import.carbon +++ b/toolchain/check/testdata/struct/import.carbon @@ -270,13 +270,13 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: %import_ref.3: %C.type = import_ref Implicit//default, C, loaded [template = constants.%C.generic] // CHECK:STDOUT: %import_ref.4: %F.type = import_ref Implicit//default, F, loaded [template = constants.%F] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Int = %import_ref.194 -// CHECK:STDOUT: .ImplicitAs = %import_ref.197 +// CHECK:STDOUT: .Int = %import_ref.230 +// CHECK:STDOUT: .ImplicitAs = %import_ref.233 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.195: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.196 = import_ref Implicit//default, inst940 [no loc], unloaded +// CHECK:STDOUT: %import_ref.231: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.232 = import_ref Implicit//default, inst988 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -309,8 +309,8 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.196 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.195 +// CHECK:STDOUT: .Self = imports.%import_ref.232 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.231 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -395,8 +395,8 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.194: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.195 = import_ref Implicit//default, inst940 [no loc], unloaded +// CHECK:STDOUT: %import_ref.230: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.231 = import_ref Implicit//default, inst988 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -423,8 +423,8 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.195 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.194 +// CHECK:STDOUT: .Self = imports.%import_ref.231 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.230 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -481,12 +481,12 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: %import_ref.3: %C.type = import_ref Implicit//default, C, loaded [template = constants.%C.generic] // CHECK:STDOUT: %import_ref.4: %F.type = import_ref Implicit//default, F, loaded [template = constants.%F] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .ImplicitAs = %import_ref.196 +// CHECK:STDOUT: .ImplicitAs = %import_ref.232 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.194: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.195 = import_ref Implicit//default, inst940 [no loc], unloaded +// CHECK:STDOUT: %import_ref.230: = import_ref Implicit//default, loc8_34, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.231 = import_ref Implicit//default, inst988 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -513,8 +513,8 @@ var c_bad: C({.a = 3, .b = 4}) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.195 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.194 +// CHECK:STDOUT: .Self = imports.%import_ref.231 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.230 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon b/toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon index 976149262c8e0..2f507ee2ec84e 100644 --- a/toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon +++ b/toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon @@ -9,9 +9,9 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon var a: (i32, i32) = (12, 6); -// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:17: error: cannot access member of interface `Core.Negate` in type `Core.IntLiteral` that does not implement that interface [MissingImplInMemberAccess] +// CHECK:STDERR: fail_negative_indexing.carbon:[[@LINE+3]]:14: error: tuple element index `-10` is past the end of type `(i32, i32)` [TupleIndexOutOfBounds] // CHECK:STDERR: var b: i32 = a.(-10); -// CHECK:STDERR: ^~~ +// CHECK:STDERR: ^~~~~~~ var b: i32 = a.(-10); // CHECK:STDOUT: --- fail_negative_indexing.carbon @@ -35,13 +35,19 @@ var b: i32 = a.(-10); // CHECK:STDOUT: %int_6.2: %i32 = int_value 6 [template] // CHECK:STDOUT: %tuple: %tuple.type.2 = tuple_value (%int_12.2, %int_6.2) [template] // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [template] +// CHECK:STDOUT: %Op.type.13: type = fn_type @Op.13 [template] +// CHECK:STDOUT: %Op.type.14: type = fn_type @Op.14 [template] +// CHECK:STDOUT: %Op.14: %Op.type.14 = struct_value () [template] +// CHECK:STDOUT: %interface.20: = interface_witness (%Op.14) [template] +// CHECK:STDOUT: %Op.bound: = bound_method %int_10, %Op.14 [template] +// CHECK:STDOUT: %int_-10: Core.IntLiteral = int_value -10 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { // CHECK:STDOUT: .Int = %import_ref.1 // CHECK:STDOUT: .ImplicitAs = %import_ref.5 -// CHECK:STDOUT: .Negate = %import_ref.193 +// CHECK:STDOUT: .Negate = %import_ref.229 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -84,6 +90,11 @@ var b: i32 = a.(-10); // CHECK:STDOUT: assign file.%a.var, %.loc11_28 // CHECK:STDOUT: %a.ref: ref %tuple.type.2 = name_ref a, file.%a // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [template = constants.%int_10] +// CHECK:STDOUT: %impl.elem0.loc15: %Op.type.13 = interface_witness_access constants.%interface.20, element0 [template = constants.%Op.14] +// CHECK:STDOUT: %Op.bound: = bound_method %int_10, %impl.elem0.loc15 [template = constants.%Op.bound] +// CHECK:STDOUT: %int.snegate: init Core.IntLiteral = call %Op.bound(%int_10) [template = constants.%int_-10] +// CHECK:STDOUT: %.loc15_17.1: Core.IntLiteral = value_of_initializer %int.snegate [template = constants.%int_-10] +// CHECK:STDOUT: %.loc15_17.2: Core.IntLiteral = converted %int.snegate, %.loc15_17.1 [template = constants.%int_-10] // CHECK:STDOUT: assign file.%b.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/tuple/access/index_not_literal.carbon b/toolchain/check/testdata/tuple/access/index_not_literal.carbon index f7aa408e6be4e..9069ce6973bac 100644 --- a/toolchain/check/testdata/tuple/access/index_not_literal.carbon +++ b/toolchain/check/testdata/tuple/access/index_not_literal.carbon @@ -61,7 +61,7 @@ var d: i32 = a.({.index = 1 as i32}.index); // CHECK:STDOUT: .Bool = %import_ref.1 // CHECK:STDOUT: .Int = %import_ref.2 // CHECK:STDOUT: .ImplicitAs = %import_ref.6 -// CHECK:STDOUT: .As = %import_ref.194 +// CHECK:STDOUT: .As = %import_ref.230 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/tuple/import.carbon b/toolchain/check/testdata/tuple/import.carbon index 3df6812364ee9..08bdb0906afad 100644 --- a/toolchain/check/testdata/tuple/import.carbon +++ b/toolchain/check/testdata/tuple/import.carbon @@ -287,13 +287,13 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: %import_ref.3: %C.type = import_ref Implicit//default, C, loaded [template = constants.%C.generic] // CHECK:STDOUT: %import_ref.4: %F.type = import_ref Implicit//default, F, loaded [template = constants.%F] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Int = %import_ref.194 -// CHECK:STDOUT: .ImplicitAs = %import_ref.197 +// CHECK:STDOUT: .Int = %import_ref.230 +// CHECK:STDOUT: .ImplicitAs = %import_ref.233 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.195: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.196 = import_ref Implicit//default, inst975 [no loc], unloaded +// CHECK:STDOUT: %import_ref.231: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.232 = import_ref Implicit//default, inst1023 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -326,8 +326,8 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.196 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.195 +// CHECK:STDOUT: .Self = imports.%import_ref.232 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.231 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -420,8 +420,8 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.194: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.195 = import_ref Implicit//default, inst975 [no loc], unloaded +// CHECK:STDOUT: %import_ref.230: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.231 = import_ref Implicit//default, inst1023 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -448,8 +448,8 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.195 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.194 +// CHECK:STDOUT: .Self = imports.%import_ref.231 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.230 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: @@ -506,12 +506,12 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: %import_ref.3: %C.type = import_ref Implicit//default, C, loaded [template = constants.%C.generic] // CHECK:STDOUT: %import_ref.4: %F.type = import_ref Implicit//default, F, loaded [template = constants.%F] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .ImplicitAs = %import_ref.196 +// CHECK:STDOUT: .ImplicitAs = %import_ref.232 // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.194: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] -// CHECK:STDOUT: %import_ref.195 = import_ref Implicit//default, inst975 [no loc], unloaded +// CHECK:STDOUT: %import_ref.230: = import_ref Implicit//default, loc7_26, loaded [template = constants.%complete_type.3] +// CHECK:STDOUT: %import_ref.231 = import_ref Implicit//default, inst1023 [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -538,8 +538,8 @@ var c_bad: C((3, 4)) = F(); // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = imports.%import_ref.195 -// CHECK:STDOUT: complete_type_witness = imports.%import_ref.194 +// CHECK:STDOUT: .Self = imports.%import_ref.231 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.230 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 457768bb6c8b1..da22969879331 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -306,7 +306,9 @@ CARBON_DIAGNOSTIC_KIND(CompileTimeDivisionByZero) CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerOverflow) CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerNegateOverflow) CARBON_DIAGNOSTIC_KIND(CompileTimeFloatBitWidth) +CARBON_DIAGNOSTIC_KIND(CompileTimeShiftNegative) CARBON_DIAGNOSTIC_KIND(CompileTimeShiftOutOfRange) +CARBON_DIAGNOSTIC_KIND(CompileTimeUnsizedShiftOutOfRange) CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop) CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType) CARBON_DIAGNOSTIC_KIND(CoreNameNotFound) diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index ee32d091a910b..cfc956579e3ef 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -48,6 +48,11 @@ class ConstantContext { return nullptr; } + // Gets the value to use for an integer literal. + auto GetIntLiteralAsValue() const -> llvm::Constant* { + return file_context_->GetIntLiteralAsValue(); + } + // Gets a callable's function. Returns nullptr for a builtin. auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* { return file_context_->GetFunction(function_id); @@ -187,9 +192,9 @@ static auto EmitAsConstant(ConstantContext& context, SemIR::IntValue inst) // represented as an LLVM integer type. auto* int_type = llvm::dyn_cast(type); if (!int_type) { - auto* struct_type = llvm::dyn_cast(type); - CARBON_CHECK(struct_type && struct_type->getNumElements() == 0); - return llvm::ConstantStruct::get(struct_type); + auto* int_literal_value = context.GetIntLiteralAsValue(); + CARBON_CHECK(int_literal_value->getType() == type); + return int_literal_value; } auto val = context.sem_ir().ints().Get(inst.int_id); diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index c2f1d57722493..2c1fd28011b6f 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -68,6 +68,12 @@ class FileContext { return llvm::ConstantStruct::get(GetTypeType()); } + // Returns a lowered value to use for a value of int literal type. + auto GetIntLiteralAsValue() -> llvm::Constant* { + // TODO: Consider adding a named struct type for integer literals. + return llvm::ConstantStruct::get(llvm::StructType::get(llvm_context())); + } + // Returns a global value for the given instruction. auto GetGlobal(SemIR::InstId inst_id) -> llvm::Value*; diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index eef1a7370cac7..de2aff947aa49 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -89,6 +89,11 @@ class FunctionContext { return file_context_->GetTypeAsValue(); } + // Returns a lowered value to use for a value of int literal type. + auto GetIntLiteralAsValue() -> llvm::Constant* { + return file_context_->GetIntLiteralAsValue(); + } + // Returns the instruction immediately after all the existing static allocas. // This is the insert point for future static allocas. auto GetInstructionAfterAllocas() const -> llvm::Instruction* { diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 9ea2dc5e6b83d..d246ed27455d5 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -65,6 +65,79 @@ static auto IsSignedInt(FunctionContext& context, SemIR::InstId int_id) context.sem_ir().insts().Get(int_id).type_id()); } +// Creates a zext or sext instruction depending on the signedness of the +// operand. +static auto CreateZExtOrSExt(FunctionContext& context, llvm::Value* value, + llvm::Type* type, bool is_signed, + const llvm::Twine& name = "") -> llvm::Value* { + return is_signed ? context.builder().CreateSExt(value, type, name) + : context.builder().CreateZExt(value, type, name); +} + +// Handles a call to a builtin integer bit shift operator. +static auto HandleIntShift(FunctionContext& context, SemIR::InstId inst_id, + llvm::Instruction::BinaryOps bin_op, + SemIR::InstId lhs_id, SemIR::InstId rhs_id) -> void { + llvm::Value* lhs = context.GetValue(lhs_id); + llvm::Value* rhs = context.GetValue(rhs_id); + + // Weirdly, LLVM requires the operands of bit shift operators to be of the + // same type. We can always use the width of the LHS, because if the RHS + // doesn't fit in that then the cast is out of range anyway. Zero-extending is + // always fine because it's an error for the RHS to be negative. + // + // TODO: In a development build we should trap if the RHS is signed and + // negative or greater than or equal to the number of bits in the left-hand + // type. + rhs = context.builder().CreateZExtOrTrunc(rhs, lhs->getType(), "rhs"); + + context.SetLocal(inst_id, context.builder().CreateBinOp(bin_op, lhs, rhs)); +} + +// Handles a call to a builtin integer comparison operator. +static auto HandleIntComparison(FunctionContext& context, SemIR::InstId inst_id, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::InstId lhs_id, SemIR::InstId rhs_id) + -> void { + llvm::Value* lhs = context.GetValue(lhs_id); + llvm::Value* rhs = context.GetValue(rhs_id); + const auto* lhs_type = cast(lhs->getType()); + const auto* rhs_type = cast(rhs->getType()); + + // We perform a signed comparison if either operand is signed. + bool lhs_signed = IsSignedInt(context, lhs_id); + bool rhs_signed = IsSignedInt(context, rhs_id); + bool cmp_signed = lhs_signed || rhs_signed; + + // Compute the width for the comparison. This is the smallest width that + // fits both types, after widening them to include a sign bit if + // necessary. + auto width_for_cmp = [&](const llvm::IntegerType* type, bool is_signed) { + unsigned width = type->getBitWidth(); + if (!is_signed && cmp_signed) { + // We're performing a signed comparison but this input is unsigned. + // Widen it by at least one bit to provide a sign bit. + ++width; + } + return width; + }; + // TODO: This might be an awkward size, such as 33 or 65 bits, for a + // signed/unsigned comparison. Would it be better to round this up to a + // "nicer" bit width? + unsigned cmp_width = std::max(width_for_cmp(lhs_type, lhs_signed), + width_for_cmp(rhs_type, rhs_signed)); + auto* cmp_type = llvm::IntegerType::get(context.llvm_context(), cmp_width); + + // Widen the operands as needed. + lhs = CreateZExtOrSExt(context, lhs, cmp_type, lhs_signed, "lhs"); + rhs = CreateZExtOrSExt(context, rhs, cmp_type, rhs_signed, "rhs"); + + context.SetLocal( + inst_id, + context.builder().CreateICmp( + GetBuiltinICmpPredicate(builtin_kind, cmp_signed), lhs, rhs)); +} + // Handles a call to a builtin function. static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, SemIR::BuiltinFunctionKind builtin_kind, @@ -245,19 +318,15 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, return; } case SemIR::BuiltinFunctionKind::IntLeftShift: { - context.SetLocal( - inst_id, context.builder().CreateShl(context.GetValue(arg_ids[0]), - context.GetValue(arg_ids[1]))); + HandleIntShift(context, inst_id, llvm::Instruction::Shl, arg_ids[0], + arg_ids[1]); return; } case SemIR::BuiltinFunctionKind::IntRightShift: { - context.SetLocal( - inst_id, - IsSignedInt(context, inst_id) - ? context.builder().CreateAShr(context.GetValue(arg_ids[0]), - context.GetValue(arg_ids[1])) - : context.builder().CreateLShr(context.GetValue(arg_ids[0]), - context.GetValue(arg_ids[1]))); + HandleIntShift(context, inst_id, + IsSignedInt(context, inst_id) ? llvm::Instruction::AShr + : llvm::Instruction::LShr, + arg_ids[0], arg_ids[1]); return; } case SemIR::BuiltinFunctionKind::IntEq: @@ -268,12 +337,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::IntGreaterEq: case SemIR::BuiltinFunctionKind::BoolEq: case SemIR::BuiltinFunctionKind::BoolNeq: { - context.SetLocal( - inst_id, - context.builder().CreateICmp( - GetBuiltinICmpPredicate(builtin_kind, - IsSignedInt(context, arg_ids[0])), - context.GetValue(arg_ids[0]), context.GetValue(arg_ids[1]))); + HandleIntComparison(context, inst_id, builtin_kind, arg_ids[0], + arg_ids[1]); return; } case SemIR::BuiltinFunctionKind::FloatNegate: { @@ -320,7 +385,9 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::IntConvertChecked: { // TODO: Check this statically. - CARBON_CHECK(builtin_kind.IsCompTimeOnly()); + CARBON_CHECK(builtin_kind.IsCompTimeOnly( + context.sem_ir(), arg_ids, + context.sem_ir().insts().Get(inst_id).type_id())); CARBON_FATAL("Missing constant value for call to comptime-only function"); } } diff --git a/toolchain/lower/testdata/builtins/int.carbon b/toolchain/lower/testdata/builtins/int.carbon index 51ee33fd7c20f..d9273ef196cef 100644 --- a/toolchain/lower/testdata/builtins/int.carbon +++ b/toolchain/lower/testdata/builtins/int.carbon @@ -8,6 +8,10 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/int.carbon +// --- basic.carbon + +library "[[@TEST_NAME]]"; + fn Negate(a: i32) -> i32 = "int.snegate"; fn TestNegate(a: i32) -> i32 { return Negate(a); } @@ -41,8 +45,11 @@ fn TestXor(a: i32, b: i32) -> i32 { return Xor(a, b); } fn LeftShift(a: i32, b: i32) -> i32 = "int.left_shift"; fn TestLeftShift(a: i32, b: i32) -> i32 { return LeftShift(a, b); } -fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; -fn TestRightShift(a: i32, b: i32) -> i32 { return RightShift(a, b); } +fn ArithmeticRightShift(a: i32, b: i32) -> i32 = "int.right_shift"; +fn TestArithmeticRightShift(a: i32, b: i32) -> i32 { return ArithmeticRightShift(a, b); } + +fn LogicalRightShift(a: u32, b: u32) -> u32 = "int.right_shift"; +fn TestLogicalRightShift(a: u32, b: u32) -> u32 { return LogicalRightShift(a, b); } fn Eq(a: i32, b: i32) -> bool = "int.eq"; fn TestEq(a: i32, b: i32) -> bool { return Eq(a, b); } @@ -62,8 +69,68 @@ fn TestGreater(a: i32, b: i32) -> bool { return Greater(a, b); } fn GreaterEq(a: i32, b: i32) -> bool = "int.greater_eq"; fn TestGreaterEq(a: i32, b: i32) -> bool { return GreaterEq(a, b); } -// CHECK:STDOUT: ; ModuleID = 'int.carbon' -// CHECK:STDOUT: source_filename = "int.carbon" +// --- mixed_shift.carbon + +library "[[@TEST_NAME]]"; + +fn LeftShiftSmaller(a: i32, b: i16) -> i32 = "int.left_shift"; +fn TestLeftShiftSmaller(a: i32, b: i16) -> i32 { return LeftShiftSmaller(a, b); } + +fn RightShiftSmaller(a: i32, b: i16) -> i32 = "int.right_shift"; +fn TestRightShiftSmaller(a: i32, b: i16) -> i32 { return RightShiftSmaller(a, b); } + +fn LeftShiftLargerII(a: i16, b: i32) -> i16 = "int.left_shift"; +fn TestLeftShiftLargerII(a: i16, b: i32) -> i16 { return LeftShiftLargerII(a, b); } + +fn RightShiftLargerII(a: i16, b: i32) -> i16 = "int.right_shift"; +fn TestRightShiftLargerII(a: i16, b: i32) -> i16 { return RightShiftLargerII(a, b); } + +fn LeftShiftLargerIU(a: i16, b: u32) -> i16 = "int.left_shift"; +fn TestLeftShiftLargerIU(a: i16, b: u32) -> i16 { return LeftShiftLargerIU(a, b); } + +fn RightShiftLargerIU(a: i16, b: u32) -> i16 = "int.right_shift"; +fn TestRightShiftLargerIU(a: i16, b: u32) -> i16 { return RightShiftLargerIU(a, b); } + +fn LeftShiftLargerUI(a: u16, b: i32) -> u16 = "int.left_shift"; +fn TestLeftShiftLargerUI(a: u16, b: i32) -> u16 { return LeftShiftLargerUI(a, b); } + +fn RightShiftLargerUI(a: u16, b: i32) -> u16 = "int.right_shift"; +fn TestRightShiftLargerUI(a: u16, b: i32) -> u16 { return RightShiftLargerUI(a, b); } + +fn LeftShiftLargerUU(a: u16, b: u32) -> u16 = "int.left_shift"; +fn TestLeftShiftLargerUU(a: u16, b: u32) -> u16 { return LeftShiftLargerUU(a, b); } + +fn RightShiftLargerUU(a: u16, b: u32) -> u16 = "int.right_shift"; +fn TestRightShiftLargerUU(a: u16, b: u32) -> u16 { return RightShiftLargerUU(a, b); } + +// --- mixed_compare.carbon + +fn Eq_u16_u32(a: u16, b: u32) -> bool = "int.eq"; +fn Eq_i16_u32(a: i16, b: u32) -> bool = "int.eq"; +fn Eq_u16_i32(a: u16, b: i32) -> bool = "int.eq"; +fn Eq_i16_i32(a: i16, b: i32) -> bool = "int.eq"; +fn Eq_i32_u32(a: i32, b: u32) -> bool = "int.eq"; + +fn TestEq_u16_u32(a: u16, b: u32) -> bool { return Eq_u16_u32(a, b); } +fn TestEq_i16_u32(a: i16, b: u32) -> bool { return Eq_i16_u32(a, b); } +fn TestEq_u16_i32(a: u16, b: i32) -> bool { return Eq_u16_i32(a, b); } +fn TestEq_i16_i32(a: i16, b: i32) -> bool { return Eq_i16_i32(a, b); } +fn TestEq_i32_u32(a: i32, b: u32) -> bool { return Eq_i32_u32(a, b); } + +fn Less_u16_u32(a: u16, b: u32) -> bool = "int.less"; +fn Less_i16_u32(a: i16, b: u32) -> bool = "int.less"; +fn Less_u16_i32(a: u16, b: i32) -> bool = "int.less"; +fn Less_i16_i32(a: i16, b: i32) -> bool = "int.less"; +fn Less_i32_u32(a: i32, b: u32) -> bool = "int.less"; + +fn TestLess_u16_u32(a: u16, b: u32) -> bool { return Less_u16_u32(a, b); } +fn TestLess_i16_u32(a: i16, b: u32) -> bool { return Less_i16_u32(a, b); } +fn TestLess_u16_i32(a: u16, b: i32) -> bool { return Less_u16_i32(a, b); } +fn TestLess_i16_i32(a: i16, b: i32) -> bool { return Less_i16_i32(a, b); } +fn TestLess_i32_u32(a: i32, b: u32) -> bool { return Less_i32_u32(a, b); } + +// CHECK:STDOUT: ; ModuleID = 'basic.carbon' +// CHECK:STDOUT: source_filename = "basic.carbon" // CHECK:STDOUT: // CHECK:STDOUT: define i32 @_CTestNegate.Main(i32 %a) !dbg !4 { // CHECK:STDOUT: entry: @@ -131,46 +198,307 @@ fn TestGreaterEq(a: i32, b: i32) -> bool { return GreaterEq(a, b); } // CHECK:STDOUT: ret i32 %int.left_shift, !dbg !38 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i32 @_CTestRightShift.Main(i32 %a, i32 %b) !dbg !39 { +// CHECK:STDOUT: define i32 @_CTestArithmeticRightShift.Main(i32 %a, i32 %b) !dbg !39 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %int.right_shift = ashr i32 %a, %b, !dbg !40 // CHECK:STDOUT: ret i32 %int.right_shift, !dbg !41 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestEq.Main(i32 %a, i32 %b) !dbg !42 { +// CHECK:STDOUT: define i32 @_CTestLogicalRightShift.Main(i32 %a, i32 %b) !dbg !42 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift = lshr i32 %a, %b, !dbg !43 +// CHECK:STDOUT: ret i32 %int.right_shift, !dbg !44 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq.Main(i32 %a, i32 %b) !dbg !45 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.eq = icmp eq i32 %a, %b, !dbg !46 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !47 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestNeq.Main(i32 %a, i32 %b) !dbg !48 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.neq = icmp ne i32 %a, %b, !dbg !49 +// CHECK:STDOUT: ret i1 %int.neq, !dbg !50 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestLess.Main(i32 %a, i32 %b) !dbg !51 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.less = icmp slt i32 %a, %b, !dbg !52 +// CHECK:STDOUT: ret i1 %int.less, !dbg !53 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestLessEq.Main(i32 %a, i32 %b) !dbg !54 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.less_eq = icmp sle i32 %a, %b, !dbg !55 +// CHECK:STDOUT: ret i1 %int.less_eq, !dbg !56 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestGreater.Main(i32 %a, i32 %b) !dbg !57 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.greater = icmp sgt i32 %a, %b, !dbg !58 +// CHECK:STDOUT: ret i1 %int.greater, !dbg !59 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestGreaterEq.Main(i32 %a, i32 %b) !dbg !60 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.greater_eq = icmp sge i32 %a, %b, !dbg !61 +// CHECK:STDOUT: ret i1 %int.greater_eq, !dbg !62 +// 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: "basic.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestNegate", linkageName: "_CTestNegate.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 5, column: 39, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 32, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestAdd", linkageName: "_CTestAdd.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 8, column: 44, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 37, scope: !9) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestSub", linkageName: "_CTestSub.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 44, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 37, scope: !12) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestMul", linkageName: "_CTestMul.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 44, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 14, column: 37, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "TestDiv", linkageName: "_CTestDiv.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 17, column: 44, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 17, column: 37, scope: !18) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "TestMod", linkageName: "_CTestMod.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !22 = !DILocation(line: 20, column: 44, scope: !21) +// CHECK:STDOUT: !23 = !DILocation(line: 20, column: 37, scope: !21) +// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "TestComplement", linkageName: "_CTestComplement.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !25 = !DILocation(line: 23, column: 43, scope: !24) +// CHECK:STDOUT: !26 = !DILocation(line: 23, column: 36, scope: !24) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "TestAnd", linkageName: "_CTestAnd.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 26, column: 44, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 26, column: 37, scope: !27) +// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "TestOr", linkageName: "_CTestOr.Main", scope: null, file: !3, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !31 = !DILocation(line: 29, column: 43, scope: !30) +// CHECK:STDOUT: !32 = !DILocation(line: 29, column: 36, scope: !30) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "TestXor", linkageName: "_CTestXor.Main", scope: null, file: !3, line: 32, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !34 = !DILocation(line: 32, column: 44, scope: !33) +// CHECK:STDOUT: !35 = !DILocation(line: 32, column: 37, scope: !33) +// CHECK:STDOUT: !36 = distinct !DISubprogram(name: "TestLeftShift", linkageName: "_CTestLeftShift.Main", scope: null, file: !3, line: 35, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !37 = !DILocation(line: 35, column: 50, scope: !36) +// CHECK:STDOUT: !38 = !DILocation(line: 35, column: 43, scope: !36) +// CHECK:STDOUT: !39 = distinct !DISubprogram(name: "TestArithmeticRightShift", linkageName: "_CTestArithmeticRightShift.Main", scope: null, file: !3, line: 38, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !40 = !DILocation(line: 38, column: 61, scope: !39) +// CHECK:STDOUT: !41 = !DILocation(line: 38, column: 54, scope: !39) +// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "TestLogicalRightShift", linkageName: "_CTestLogicalRightShift.Main", scope: null, file: !3, line: 41, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !43 = !DILocation(line: 41, column: 58, scope: !42) +// CHECK:STDOUT: !44 = !DILocation(line: 41, column: 51, scope: !42) +// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "TestEq", linkageName: "_CTestEq.Main", scope: null, file: !3, line: 44, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !46 = !DILocation(line: 44, column: 44, scope: !45) +// CHECK:STDOUT: !47 = !DILocation(line: 44, column: 37, scope: !45) +// CHECK:STDOUT: !48 = distinct !DISubprogram(name: "TestNeq", linkageName: "_CTestNeq.Main", scope: null, file: !3, line: 47, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !49 = !DILocation(line: 47, column: 45, scope: !48) +// CHECK:STDOUT: !50 = !DILocation(line: 47, column: 38, scope: !48) +// CHECK:STDOUT: !51 = distinct !DISubprogram(name: "TestLess", linkageName: "_CTestLess.Main", scope: null, file: !3, line: 50, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !52 = !DILocation(line: 50, column: 46, scope: !51) +// CHECK:STDOUT: !53 = !DILocation(line: 50, column: 39, scope: !51) +// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "TestLessEq", linkageName: "_CTestLessEq.Main", scope: null, file: !3, line: 53, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !55 = !DILocation(line: 53, column: 48, scope: !54) +// CHECK:STDOUT: !56 = !DILocation(line: 53, column: 41, scope: !54) +// CHECK:STDOUT: !57 = distinct !DISubprogram(name: "TestGreater", linkageName: "_CTestGreater.Main", scope: null, file: !3, line: 56, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !58 = !DILocation(line: 56, column: 49, scope: !57) +// CHECK:STDOUT: !59 = !DILocation(line: 56, column: 42, scope: !57) +// CHECK:STDOUT: !60 = distinct !DISubprogram(name: "TestGreaterEq", linkageName: "_CTestGreaterEq.Main", scope: null, file: !3, line: 59, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !61 = !DILocation(line: 59, column: 51, scope: !60) +// CHECK:STDOUT: !62 = !DILocation(line: 59, column: 44, scope: !60) +// CHECK:STDOUT: ; ModuleID = 'mixed_shift.carbon' +// CHECK:STDOUT: source_filename = "mixed_shift.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CTestLeftShiftSmaller.Main(i32 %a, i16 %b) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.left_shift.rhs = zext i16 %b to i32, !dbg !7 +// CHECK:STDOUT: %int.left_shift = shl i32 %a, %int.left_shift.rhs, !dbg !7 +// CHECK:STDOUT: ret i32 %int.left_shift, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CTestRightShiftSmaller.Main(i32 %a, i16 %b) !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift.rhs = zext i16 %b to i32, !dbg !10 +// CHECK:STDOUT: %int.right_shift = ashr i32 %a, %int.right_shift.rhs, !dbg !10 +// CHECK:STDOUT: ret i32 %int.right_shift, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestLeftShiftLargerII.Main(i16 %a, i32 %b) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.left_shift.rhs = trunc i32 %b to i16, !dbg !13 +// CHECK:STDOUT: %int.left_shift = shl i16 %a, %int.left_shift.rhs, !dbg !13 +// CHECK:STDOUT: ret i16 %int.left_shift, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestRightShiftLargerII.Main(i16 %a, i32 %b) !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift.rhs = trunc i32 %b to i16, !dbg !16 +// CHECK:STDOUT: %int.right_shift = ashr i16 %a, %int.right_shift.rhs, !dbg !16 +// CHECK:STDOUT: ret i16 %int.right_shift, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestLeftShiftLargerIU.Main(i16 %a, i32 %b) !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.left_shift.rhs = trunc i32 %b to i16, !dbg !19 +// CHECK:STDOUT: %int.left_shift = shl i16 %a, %int.left_shift.rhs, !dbg !19 +// CHECK:STDOUT: ret i16 %int.left_shift, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestRightShiftLargerIU.Main(i16 %a, i32 %b) !dbg !21 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift.rhs = trunc i32 %b to i16, !dbg !22 +// CHECK:STDOUT: %int.right_shift = ashr i16 %a, %int.right_shift.rhs, !dbg !22 +// CHECK:STDOUT: ret i16 %int.right_shift, !dbg !23 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestLeftShiftLargerUI.Main(i16 %a, i32 %b) !dbg !24 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.left_shift.rhs = trunc i32 %b to i16, !dbg !25 +// CHECK:STDOUT: %int.left_shift = shl i16 %a, %int.left_shift.rhs, !dbg !25 +// CHECK:STDOUT: ret i16 %int.left_shift, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestRightShiftLargerUI.Main(i16 %a, i32 %b) !dbg !27 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift.rhs = trunc i32 %b to i16, !dbg !28 +// CHECK:STDOUT: %int.right_shift = lshr i16 %a, %int.right_shift.rhs, !dbg !28 +// CHECK:STDOUT: ret i16 %int.right_shift, !dbg !29 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestLeftShiftLargerUU.Main(i16 %a, i32 %b) !dbg !30 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.left_shift.rhs = trunc i32 %b to i16, !dbg !31 +// CHECK:STDOUT: %int.left_shift = shl i16 %a, %int.left_shift.rhs, !dbg !31 +// CHECK:STDOUT: ret i16 %int.left_shift, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @_CTestRightShiftLargerUU.Main(i16 %a, i32 %b) !dbg !33 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.right_shift.rhs = trunc i32 %b to i16, !dbg !34 +// CHECK:STDOUT: %int.right_shift = lshr i16 %a, %int.right_shift.rhs, !dbg !34 +// CHECK:STDOUT: ret i16 %int.right_shift, !dbg !35 +// 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: "mixed_shift.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestLeftShiftSmaller", linkageName: "_CTestLeftShiftSmaller.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 5, column: 57, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 50, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestRightShiftSmaller", linkageName: "_CTestRightShiftSmaller.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 8, column: 58, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 51, scope: !9) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestLeftShiftLargerII", linkageName: "_CTestLeftShiftLargerII.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 58, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 51, scope: !12) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestRightShiftLargerII", linkageName: "_CTestRightShiftLargerII.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 59, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 14, column: 52, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "TestLeftShiftLargerIU", linkageName: "_CTestLeftShiftLargerIU.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 17, column: 58, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 17, column: 51, scope: !18) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "TestRightShiftLargerIU", linkageName: "_CTestRightShiftLargerIU.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !22 = !DILocation(line: 20, column: 59, scope: !21) +// CHECK:STDOUT: !23 = !DILocation(line: 20, column: 52, scope: !21) +// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "TestLeftShiftLargerUI", linkageName: "_CTestLeftShiftLargerUI.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !25 = !DILocation(line: 23, column: 58, scope: !24) +// CHECK:STDOUT: !26 = !DILocation(line: 23, column: 51, scope: !24) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "TestRightShiftLargerUI", linkageName: "_CTestRightShiftLargerUI.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 26, column: 59, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 26, column: 52, scope: !27) +// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "TestLeftShiftLargerUU", linkageName: "_CTestLeftShiftLargerUU.Main", scope: null, file: !3, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !31 = !DILocation(line: 29, column: 58, scope: !30) +// CHECK:STDOUT: !32 = !DILocation(line: 29, column: 51, scope: !30) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "TestRightShiftLargerUU", linkageName: "_CTestRightShiftLargerUU.Main", scope: null, file: !3, line: 32, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !34 = !DILocation(line: 32, column: 59, scope: !33) +// CHECK:STDOUT: !35 = !DILocation(line: 32, column: 52, scope: !33) +// CHECK:STDOUT: ; ModuleID = 'mixed_compare.carbon' +// CHECK:STDOUT: source_filename = "mixed_compare.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq_u16_u32.Main(i16 %a, i32 %b) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.eq.lhs = zext i16 %a to i32, !dbg !7 +// CHECK:STDOUT: %int.eq = icmp eq i32 %int.eq.lhs, %b, !dbg !7 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq_i16_u32.Main(i16 %a, i32 %b) !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.eq.lhs = sext i16 %a to i33, !dbg !10 +// CHECK:STDOUT: %int.eq.rhs = zext i32 %b to i33, !dbg !10 +// CHECK:STDOUT: %int.eq = icmp eq i33 %int.eq.lhs, %int.eq.rhs, !dbg !10 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq_u16_i32.Main(i16 %a, i32 %b) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.eq.lhs = zext i16 %a to i32, !dbg !13 +// CHECK:STDOUT: %int.eq = icmp eq i32 %int.eq.lhs, %b, !dbg !13 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq_i16_i32.Main(i16 %a, i32 %b) !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %int.eq.lhs = sext i16 %a to i32, !dbg !16 +// CHECK:STDOUT: %int.eq = icmp eq i32 %int.eq.lhs, %b, !dbg !16 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @_CTestEq_i32_u32.Main(i32 %a, i32 %b) !dbg !18 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.eq = icmp eq i32 %a, %b, !dbg !43 -// CHECK:STDOUT: ret i1 %int.eq, !dbg !44 +// CHECK:STDOUT: %int.eq.lhs = sext i32 %a to i33, !dbg !19 +// CHECK:STDOUT: %int.eq.rhs = zext i32 %b to i33, !dbg !19 +// CHECK:STDOUT: %int.eq = icmp eq i33 %int.eq.lhs, %int.eq.rhs, !dbg !19 +// CHECK:STDOUT: ret i1 %int.eq, !dbg !20 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestNeq.Main(i32 %a, i32 %b) !dbg !45 { +// CHECK:STDOUT: define i1 @_CTestLess_u16_u32.Main(i16 %a, i32 %b) !dbg !21 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.neq = icmp ne i32 %a, %b, !dbg !46 -// CHECK:STDOUT: ret i1 %int.neq, !dbg !47 +// CHECK:STDOUT: %int.less.lhs = zext i16 %a to i32, !dbg !22 +// CHECK:STDOUT: %int.less = icmp ult i32 %int.less.lhs, %b, !dbg !22 +// CHECK:STDOUT: ret i1 %int.less, !dbg !23 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestLess.Main(i32 %a, i32 %b) !dbg !48 { +// CHECK:STDOUT: define i1 @_CTestLess_i16_u32.Main(i16 %a, i32 %b) !dbg !24 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.less = icmp slt i32 %a, %b, !dbg !49 -// CHECK:STDOUT: ret i1 %int.less, !dbg !50 +// CHECK:STDOUT: %int.less.lhs = sext i16 %a to i33, !dbg !25 +// CHECK:STDOUT: %int.less.rhs = zext i32 %b to i33, !dbg !25 +// CHECK:STDOUT: %int.less = icmp slt i33 %int.less.lhs, %int.less.rhs, !dbg !25 +// CHECK:STDOUT: ret i1 %int.less, !dbg !26 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestLessEq.Main(i32 %a, i32 %b) !dbg !51 { +// CHECK:STDOUT: define i1 @_CTestLess_u16_i32.Main(i16 %a, i32 %b) !dbg !27 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.less_eq = icmp sle i32 %a, %b, !dbg !52 -// CHECK:STDOUT: ret i1 %int.less_eq, !dbg !53 +// CHECK:STDOUT: %int.less.lhs = zext i16 %a to i32, !dbg !28 +// CHECK:STDOUT: %int.less = icmp slt i32 %int.less.lhs, %b, !dbg !28 +// CHECK:STDOUT: ret i1 %int.less, !dbg !29 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestGreater.Main(i32 %a, i32 %b) !dbg !54 { +// CHECK:STDOUT: define i1 @_CTestLess_i16_i32.Main(i16 %a, i32 %b) !dbg !30 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.greater = icmp sgt i32 %a, %b, !dbg !55 -// CHECK:STDOUT: ret i1 %int.greater, !dbg !56 +// CHECK:STDOUT: %int.less.lhs = sext i16 %a to i32, !dbg !31 +// CHECK:STDOUT: %int.less = icmp slt i32 %int.less.lhs, %b, !dbg !31 +// CHECK:STDOUT: ret i1 %int.less, !dbg !32 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: define i1 @_CTestGreaterEq.Main(i32 %a, i32 %b) !dbg !57 { +// CHECK:STDOUT: define i1 @_CTestLess_i32_u32.Main(i32 %a, i32 %b) !dbg !33 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %int.greater_eq = icmp sge i32 %a, %b, !dbg !58 -// CHECK:STDOUT: ret i1 %int.greater_eq, !dbg !59 +// CHECK:STDOUT: %int.less.lhs = sext i32 %a to i33, !dbg !34 +// CHECK:STDOUT: %int.less.rhs = zext i32 %b to i33, !dbg !34 +// CHECK:STDOUT: %int.less = icmp slt i33 %int.less.lhs, %int.less.rhs, !dbg !34 +// CHECK:STDOUT: ret i1 %int.less, !dbg !35 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} @@ -179,60 +507,36 @@ fn TestGreaterEq(a: i32, b: i32) -> bool { return GreaterEq(a, b); } // 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: "int.carbon", directory: "") -// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestNegate", linkageName: "_CTestNegate.Main", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !3 = !DIFile(filename: "mixed_compare.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestEq_u16_u32", linkageName: "_CTestEq_u16_u32.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{} -// CHECK:STDOUT: !7 = !DILocation(line: 12, column: 39, scope: !4) -// CHECK:STDOUT: !8 = !DILocation(line: 12, column: 32, scope: !4) -// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestAdd", linkageName: "_CTestAdd.Main", scope: null, file: !3, line: 15, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !10 = !DILocation(line: 15, column: 44, scope: !9) -// CHECK:STDOUT: !11 = !DILocation(line: 15, column: 37, scope: !9) -// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestSub", linkageName: "_CTestSub.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !13 = !DILocation(line: 18, column: 44, scope: !12) -// CHECK:STDOUT: !14 = !DILocation(line: 18, column: 37, scope: !12) -// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestMul", linkageName: "_CTestMul.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !16 = !DILocation(line: 21, column: 44, scope: !15) -// CHECK:STDOUT: !17 = !DILocation(line: 21, column: 37, scope: !15) -// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "TestDiv", linkageName: "_CTestDiv.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !19 = !DILocation(line: 24, column: 44, scope: !18) -// CHECK:STDOUT: !20 = !DILocation(line: 24, column: 37, scope: !18) -// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "TestMod", linkageName: "_CTestMod.Main", scope: null, file: !3, line: 27, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !22 = !DILocation(line: 27, column: 44, scope: !21) -// CHECK:STDOUT: !23 = !DILocation(line: 27, column: 37, scope: !21) -// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "TestComplement", linkageName: "_CTestComplement.Main", scope: null, file: !3, line: 30, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !25 = !DILocation(line: 30, column: 43, scope: !24) -// CHECK:STDOUT: !26 = !DILocation(line: 30, column: 36, scope: !24) -// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "TestAnd", linkageName: "_CTestAnd.Main", scope: null, file: !3, line: 33, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !28 = !DILocation(line: 33, column: 44, scope: !27) -// CHECK:STDOUT: !29 = !DILocation(line: 33, column: 37, scope: !27) -// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "TestOr", linkageName: "_CTestOr.Main", scope: null, file: !3, line: 36, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !31 = !DILocation(line: 36, column: 43, scope: !30) -// CHECK:STDOUT: !32 = !DILocation(line: 36, column: 36, scope: !30) -// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "TestXor", linkageName: "_CTestXor.Main", scope: null, file: !3, line: 39, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !34 = !DILocation(line: 39, column: 44, scope: !33) -// CHECK:STDOUT: !35 = !DILocation(line: 39, column: 37, scope: !33) -// CHECK:STDOUT: !36 = distinct !DISubprogram(name: "TestLeftShift", linkageName: "_CTestLeftShift.Main", scope: null, file: !3, line: 42, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !37 = !DILocation(line: 42, column: 50, scope: !36) -// CHECK:STDOUT: !38 = !DILocation(line: 42, column: 43, scope: !36) -// CHECK:STDOUT: !39 = distinct !DISubprogram(name: "TestRightShift", linkageName: "_CTestRightShift.Main", scope: null, file: !3, line: 45, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !40 = !DILocation(line: 45, column: 51, scope: !39) -// CHECK:STDOUT: !41 = !DILocation(line: 45, column: 44, scope: !39) -// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "TestEq", linkageName: "_CTestEq.Main", scope: null, file: !3, line: 48, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !43 = !DILocation(line: 48, column: 44, scope: !42) -// CHECK:STDOUT: !44 = !DILocation(line: 48, column: 37, scope: !42) -// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "TestNeq", linkageName: "_CTestNeq.Main", scope: null, file: !3, line: 51, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !46 = !DILocation(line: 51, column: 45, scope: !45) -// CHECK:STDOUT: !47 = !DILocation(line: 51, column: 38, scope: !45) -// CHECK:STDOUT: !48 = distinct !DISubprogram(name: "TestLess", linkageName: "_CTestLess.Main", scope: null, file: !3, line: 54, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !49 = !DILocation(line: 54, column: 46, scope: !48) -// CHECK:STDOUT: !50 = !DILocation(line: 54, column: 39, scope: !48) -// CHECK:STDOUT: !51 = distinct !DISubprogram(name: "TestLessEq", linkageName: "_CTestLessEq.Main", scope: null, file: !3, line: 57, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !52 = !DILocation(line: 57, column: 48, scope: !51) -// CHECK:STDOUT: !53 = !DILocation(line: 57, column: 41, scope: !51) -// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "TestGreater", linkageName: "_CTestGreater.Main", scope: null, file: !3, line: 60, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !55 = !DILocation(line: 60, column: 49, scope: !54) -// CHECK:STDOUT: !56 = !DILocation(line: 60, column: 42, scope: !54) -// CHECK:STDOUT: !57 = distinct !DISubprogram(name: "TestGreaterEq", linkageName: "_CTestGreaterEq.Main", scope: null, file: !3, line: 63, type: !5, spFlags: DISPFlagDefinition, unit: !2) -// CHECK:STDOUT: !58 = !DILocation(line: 63, column: 51, scope: !57) -// CHECK:STDOUT: !59 = !DILocation(line: 63, column: 44, scope: !57) +// CHECK:STDOUT: !7 = !DILocation(line: 8, column: 52, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 45, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestEq_i16_u32", linkageName: "_CTestEq_i16_u32.Main", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !10 = !DILocation(line: 9, column: 52, scope: !9) +// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 45, scope: !9) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestEq_u16_i32", linkageName: "_CTestEq_u16_i32.Main", scope: null, file: !3, line: 10, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 10, column: 52, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 10, column: 45, scope: !12) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestEq_i16_i32", linkageName: "_CTestEq_i16_i32.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !16 = !DILocation(line: 11, column: 52, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 11, column: 45, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "TestEq_i32_u32", linkageName: "_CTestEq_i32_u32.Main", scope: null, file: !3, line: 12, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 12, column: 52, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 12, column: 45, scope: !18) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "TestLess_u16_u32", linkageName: "_CTestLess_u16_u32.Main", scope: null, file: !3, line: 20, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !22 = !DILocation(line: 20, column: 54, scope: !21) +// CHECK:STDOUT: !23 = !DILocation(line: 20, column: 47, scope: !21) +// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "TestLess_i16_u32", linkageName: "_CTestLess_i16_u32.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !25 = !DILocation(line: 21, column: 54, scope: !24) +// CHECK:STDOUT: !26 = !DILocation(line: 21, column: 47, scope: !24) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "TestLess_u16_i32", linkageName: "_CTestLess_u16_i32.Main", scope: null, file: !3, line: 22, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 22, column: 54, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 22, column: 47, scope: !27) +// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "TestLess_i16_i32", linkageName: "_CTestLess_i16_i32.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !31 = !DILocation(line: 23, column: 54, scope: !30) +// CHECK:STDOUT: !32 = !DILocation(line: 23, column: 47, scope: !30) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "TestLess_i32_u32", linkageName: "_CTestLess_i32_u32.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !34 = !DILocation(line: 24, column: 54, scope: !33) +// CHECK:STDOUT: !35 = !DILocation(line: 24, column: 47, scope: !33) diff --git a/toolchain/lower/testdata/builtins/int_literal.carbon b/toolchain/lower/testdata/builtins/int_literal.carbon index afe16953ddbf9..db93678405de3 100644 --- a/toolchain/lower/testdata/builtins/int_literal.carbon +++ b/toolchain/lower/testdata/builtins/int_literal.carbon @@ -14,6 +14,18 @@ fn Copy(x: Make()) -> Make() { return x; } +fn MinusOne() -> i32 { + return -1; +} + +fn IntMax() -> i32 { + return 0x1_0000_0000_0000_0000 / 0x2_0000_0000 - 1; +} + +fn IntMin() -> i32 { + return -0x8000_0000; +} + // CHECK:STDOUT: ; ModuleID = 'int_literal.carbon' // CHECK:STDOUT: source_filename = "int_literal.carbon" // CHECK:STDOUT: @@ -22,6 +34,21 @@ fn Copy(x: Make()) -> Make() { // CHECK:STDOUT: ret {} %x, !dbg !7 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CMinusOne.Main() !dbg !8 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 -1, !dbg !9 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CIntMax.Main() !dbg !10 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 2147483647, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CIntMin.Main() !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 -2147483648, !dbg !13 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} // CHECK:STDOUT: @@ -33,3 +60,9 @@ fn Copy(x: Make()) -> Make() { // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{} // CHECK:STDOUT: !7 = !DILocation(line: 14, column: 3, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "MinusOne", linkageName: "_CMinusOne.Main", scope: null, file: !3, line: 17, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 18, column: 3, scope: !8) +// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "IntMax", linkageName: "_CIntMax.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !11 = !DILocation(line: 22, column: 3, scope: !10) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "IntMin", linkageName: "_CIntMin.Main", scope: null, file: !3, line: 25, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 26, column: 3, scope: !12) diff --git a/toolchain/lower/testdata/builtins/print_read.carbon b/toolchain/lower/testdata/builtins/print_read.carbon index 8834eea2d5190..5e2b582f39774 100644 --- a/toolchain/lower/testdata/builtins/print_read.carbon +++ b/toolchain/lower/testdata/builtins/print_read.carbon @@ -16,7 +16,7 @@ fn ReadChar() -> i32 = "read.char"; fn Main() { Core.Print(1); - let EOF: i32 = -(1 as i32); + let EOF: i32 = -1; while (ReadChar() != EOF) { // "Hi" if (PrintChar(0x48) != EOF) { diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 3146f6c55f8f0..875165cf6ef9f 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -79,15 +79,21 @@ struct NoReturn { // Constraint that a type is `bool`. using Bool = BuiltinType; +// Constraint that requires the type to be a sized integer type. +struct AnySizedInt { + static auto Check(const File& sem_ir, ValidateState& /*state*/, + TypeId type_id) -> bool { + return sem_ir.types().Is(type_id); + } +}; + // Constraint that requires the type to be an integer type. struct AnyInt { static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool { - if (BuiltinType::Check(sem_ir, state, - type_id)) { - return true; - } - return sem_ir.types().Is(type_id); + return AnySizedInt::Check(sem_ir, state, type_id) || + BuiltinType::Check(sem_ir, state, + type_id); } }; @@ -188,6 +194,10 @@ using IntT = TypeParam<0, AnyInt>; // generic type parameter that is constrained to be an integer type. using IntU = TypeParam<1, AnyInt>; +// Convenience name used in the builtin type signatures below for a first +// generic type parameter that is constrained to be a sized integer type. +using SizedIntT = TypeParam<0, AnySizedInt>; + // Convenience name used in the builtin type signatures below for a first // generic type parameter that is constrained to be an float type. using FloatT = TypeParam<0, AnyFloat>; @@ -196,16 +206,16 @@ using FloatT = TypeParam<0, AnyFloat>; constexpr BuiltinInfo None = {"", nullptr}; // Prints a single character. -constexpr BuiltinInfo PrintChar = {"print.char", - ValidateSignatureAnyInt>}; +constexpr BuiltinInfo PrintChar = { + "print.char", ValidateSignatureAnySizedInt>}; // Prints an integer. -constexpr BuiltinInfo PrintInt = {"print.int", - ValidateSignatureNoReturn>}; +constexpr BuiltinInfo PrintInt = { + "print.int", ValidateSignatureNoReturn>}; // Reads a single character from stdin. constexpr BuiltinInfo ReadChar = {"read.char", - ValidateSignatureAnyInt>}; + ValidateSignatureAnySizedInt>}; // Returns the `Core.IntLiteral` type. constexpr BuiltinInfo IntLiteralMakeType = {"int_literal.make_type", @@ -257,28 +267,28 @@ constexpr BuiltinInfo IntSMod = {"int.smod", ValidateSignatureIntT>}; // "int.unegate": unsigned integer negation. -constexpr BuiltinInfo IntUNegate = {"int.unegate", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUNegate = { + "int.unegate", ValidateSignatureSizedIntT>}; // "int.uadd": unsigned integer addition. -constexpr BuiltinInfo IntUAdd = {"int.uadd", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUAdd = { + "int.uadd", ValidateSignatureSizedIntT>}; // "int.usub": unsigned integer subtraction. -constexpr BuiltinInfo IntUSub = {"int.usub", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUSub = { + "int.usub", ValidateSignatureSizedIntT>}; // "int.umul": unsigned integer multiplication. -constexpr BuiltinInfo IntUMul = {"int.umul", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUMul = { + "int.umul", ValidateSignatureSizedIntT>}; // "int.udiv": unsigned integer division. -constexpr BuiltinInfo IntUDiv = {"int.udiv", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUDiv = { + "int.udiv", ValidateSignatureSizedIntT>}; // "int.mod": integer modulo. -constexpr BuiltinInfo IntUMod = {"int.umod", - ValidateSignatureIntT>}; +constexpr BuiltinInfo IntUMod = { + "int.umod", ValidateSignatureSizedIntT>}; // "int.complement": integer bitwise complement. constexpr BuiltinInfo IntComplement = {"int.complement", @@ -306,27 +316,27 @@ constexpr BuiltinInfo IntRightShift = { // "int.eq": integer equality comparison. constexpr BuiltinInfo IntEq = {"int.eq", - ValidateSignatureBool>}; + ValidateSignatureBool>}; // "int.neq": integer non-equality comparison. constexpr BuiltinInfo IntNeq = {"int.neq", - ValidateSignatureBool>}; + ValidateSignatureBool>}; // "int.less": integer less than comparison. constexpr BuiltinInfo IntLess = {"int.less", - ValidateSignatureBool>}; + ValidateSignatureBool>}; // "int.less_eq": integer less than or equal comparison. constexpr BuiltinInfo IntLessEq = {"int.less_eq", - ValidateSignatureBool>}; + ValidateSignatureBool>}; // "int.greater": integer greater than comparison. constexpr BuiltinInfo IntGreater = {"int.greater", - ValidateSignatureBool>}; + ValidateSignatureBool>}; // "int.greater_eq": integer greater than or equal comparison. constexpr BuiltinInfo IntGreaterEq = { - "int.greater_eq", ValidateSignatureBool>}; + "int.greater_eq", ValidateSignatureBool>}; // "float.negate": float negation. constexpr BuiltinInfo FloatNegate = {"float.negate", @@ -411,8 +421,82 @@ auto BuiltinFunctionKind::IsValidType(const File& sem_ir, return ValidateFns[AsInt()](sem_ir, arg_types, return_type); } -auto BuiltinFunctionKind::IsCompTimeOnly() const -> bool { - return *this == BuiltinFunctionKind::IntConvertChecked; +auto BuiltinFunctionKind::IsCompTimeOnly(const File& sem_ir, + llvm::ArrayRef arg_ids, + TypeId return_type_id) const -> bool { + // Some builtin functions are unconditionally compile-time-only, or + // unconditionally usable at runtime. However, we need to take extra care for + // builtins operating on an arbitrary integer type, because `Core.IntLiteral` + // has an empty runtime representation and a value of that type isn't + // necessarily a compile-time constant. For example, given: + // + // var n: Core.IntLiteral() = 123; + // + // we would be unable to lower a runtime operation such as `(1 as i32) << n` + // because the runtime representation of `n` doesn't track its value at all. + // So we treat operations involving `Core.IntLiteral` as being + // compile-time-only. + switch (*this) { + case IntConvertChecked: + // Checked integer conversions are compile-time only. + return true; + + case IntSNegate: + case IntComplement: + case IntSAdd: + case IntSSub: + case IntSMul: + case IntSDiv: + case IntSMod: + case IntAnd: + case IntOr: + case IntXor: + // Integer builtins producing an IntLiteral are compile-time only. + // TODO: We could allow these at runtime and just produce an empty struct + // result. Should we? + return sem_ir.types().Is(return_type_id); + + case IntLeftShift: + case IntRightShift: + // Shifts by an integer literal amount are compile-time only. We don't + // have a value for the shift amount at runtime in general. + // TODO: Decide how shifting a non-literal by a literal amount should + // work. We could support these with a builtin in the case where the shift + // amount has a compile-time value, or we could perform a conversion in + // the prelude. + if (sem_ir.types().Is( + sem_ir.insts().Get(arg_ids[1]).type_id())) { + return true; + } + + // Integer builtins producing an IntLiteral are compile-time only. + // TODO: We could allow these at runtime and just produce an empty struct + // result. Should we? + return sem_ir.types().Is(return_type_id); + + case IntEq: + case IntNeq: + case IntLess: + case IntLessEq: + case IntGreater: + case IntGreaterEq: + // Comparisons involving an integer literal operand are compile-time only. + // We don't have a value for an integer literal operand argument at + // runtime in general. + // TODO: Figure out how mixed literal / non-literal comparisons should + // work. We could support these with builtins in the case where the + // operand has a compile-time value, or we could perform a conversion in + // the prelude. + return sem_ir.types().Is( + sem_ir.insts().Get(arg_ids[0]).type_id()) || + sem_ir.types().Is( + sem_ir.insts().Get(arg_ids[1]).type_id()); + + default: + // TODO: Should the sized MakeType functions be compile-time only? We + // can't produce diagnostics for bad sizes at runtime. + return false; + } } } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/builtin_function_kind.h b/toolchain/sem_ir/builtin_function_kind.h index ab1ba4507f886..7207a31657ea6 100644 --- a/toolchain/sem_ir/builtin_function_kind.h +++ b/toolchain/sem_ir/builtin_function_kind.h @@ -37,7 +37,8 @@ class BuiltinFunctionKind : public CARBON_ENUM_BASE(BuiltinFunctionKind) { TypeId return_type) const -> bool; // Returns whether this is a compile-time-only function. - auto IsCompTimeOnly() const -> bool; + auto IsCompTimeOnly(const File& sem_ir, llvm::ArrayRef arg_ids, + TypeId return_type_id) const -> bool; }; #define CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(Name) \