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) \