diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index c9909d19ac92..27a6a24a6c55 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -1049,6 +1049,7 @@ class TypeCompleter { case SemIR::AssociatedEntityType::Kind: case SemIR::BindSymbolicName::Kind: + case SemIR::IntType::Kind: case SemIR::PointerType::Kind: case SemIR::UnboundElementType::Kind: return MakeCopyValueRepr(type_id); diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index d04ac612873f..4b439c1cef27 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -302,6 +302,56 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst) return context.constant_values().Get(elements[index_val.getZExtValue()]); } +// Enforces that an integer type has a valid bit width. +auto ValidateIntType(Context& context, SemIRLoc loc, SemIR::IntType result) + -> bool { + auto bit_width = + context.insts().TryGetAs(result.bit_width_id); + if (!bit_width) { + // Symbolic bit width. + return true; + } + const auto& bit_width_val = context.ints().Get(bit_width->int_id); + if (bit_width_val.isZero() || + (context.types().IsSignedInt(bit_width->type_id) && + bit_width_val.isNegative())) { + CARBON_DIAGNOSTIC(IntWidthNotPositive, Error, + "Integer type width of {0} is not positive.", TypedInt); + context.emitter().Emit(loc, IntWidthNotPositive, + TypedInt{bit_width->type_id, 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)) { + 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, + TypedInt{bit_width->type_id, bit_width_val}, + MaxIntWidth); + return false; + } + return true; +} + +// Forms a constant int type as an evaluation result. Requires that width_id is +// constant. +auto MakeIntTypeResult(Context& context, SemIRLoc loc, SemIR::IntKind int_kind, + SemIR::InstId width_id, Phase phase) + -> SemIR::ConstantId { + auto result = SemIR::IntType{ + .type_id = context.GetBuiltinType(SemIR::BuiltinKind::TypeType), + .int_kind = int_kind, + .bit_width_id = width_id}; + if (!ValidateIntType(context, loc, result)) { + return SemIR::ConstantId::Error; + } + return MakeConstantResult(context, result, phase); +} + // Enforces that the bit width is 64 for a float. static auto ValidateFloatBitWidth(Context& context, SemIRLoc loc, SemIR::InstId inst_id) -> bool { @@ -514,6 +564,16 @@ static auto PerformBuiltinCall(Context& context, SemIRLoc loc, SemIR::Call call, return context.constant_values().Get(SemIR::InstId::BuiltinIntType); } + case SemIR::BuiltinFunctionKind::IntMakeTypeSigned: { + return MakeIntTypeResult(context, loc, SemIR::IntKind::Signed, arg_ids[0], + phase); + } + + case SemIR::BuiltinFunctionKind::IntMakeTypeUnsigned: { + return MakeIntTypeResult(context, loc, SemIR::IntKind::Unsigned, + arg_ids[0], phase); + } + case SemIR::BuiltinFunctionKind::FloatMakeType: { // TODO: Support a symbolic constant width. if (phase != Phase::Template) { @@ -663,6 +723,14 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) case SemIR::InterfaceWitness::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::InterfaceWitness::elements_id); + case SemIR::IntType::Kind: + return RebuildAndValidateIfFieldsAreConstant( + context, inst, + [&](SemIR::IntType result) { + return ValidateIntType( + context, inst.As().bit_width_id, result); + }, + &SemIR::IntType::bit_width_id); case SemIR::PointerType::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::PointerType::pointee_id); diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index 92d0711e8618..0461214c4ad8 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -4,6 +4,7 @@ #include "toolchain/check/call.h" #include "toolchain/check/context.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -25,27 +26,34 @@ auto HandleBoolLiteralTrue(Context& context, Parse::BoolLiteralTrueId node_id) return true; } -auto HandleIntLiteral(Context& context, Parse::IntLiteralId node_id) -> bool { - // Convert the literal to i32. - // TODO: Form an integer literal value and a corresponding type here instead. - auto literal_int_id = - context.tokens().GetIntLiteral(context.parse_tree().node_token(node_id)); - auto literal_val = context.ints().Get(literal_int_id); - if (literal_val.getActiveBits() > 31) { +// Forms an IntLiteral instruction with type `i32` for a given literal integer +// value, which is assumed to be unsigned. +static auto MakeI32Literal(Context& context, Parse::NodeId node_id, + IntId int_id) -> SemIR::InstId { + auto val = context.ints().Get(int_id); + if (val.getActiveBits() > 31) { CARBON_DIAGNOSTIC(IntLiteralTooLargeForI32, Error, "Integer literal with value {0} does not fit in i32.", llvm::APSInt); context.emitter().Emit(node_id, IntLiteralTooLargeForI32, - llvm::APSInt(literal_val, /*isUnsigned=*/true)); - context.node_stack().Push(node_id, SemIR::InstId::BuiltinError); - return true; + llvm::APSInt(val, /*isUnsigned=*/true)); + return SemIR::InstId::BuiltinError; } // Literals are always represented as unsigned, so zero-extend if needed. - auto i32_val = literal_val.zextOrTrunc(32); - context.AddInstAndPush( + auto i32_val = val.zextOrTrunc(32); + return context.AddInst( {node_id, SemIR::IntLiteral{context.GetBuiltinType(SemIR::BuiltinKind::IntType), context.ints().Add(i32_val)}}); +} + +auto HandleIntLiteral(Context& context, Parse::IntLiteralId node_id) -> bool { + // Convert the literal to i32. + // TODO: Form an integer literal value and a corresponding type here instead. + auto int_literal_id = MakeI32Literal( + context, node_id, + context.tokens().GetIntLiteral(context.parse_tree().node_token(node_id))); + context.node_stack().Push(node_id, int_literal_id); return true; } @@ -78,25 +86,51 @@ auto HandleBoolTypeLiteral(Context& context, Parse::BoolTypeLiteralId node_id) return true; } +// Shared implementation for handling `iN` and `uN` literals. +static auto HandleIntOrUnsignedIntTypeLiteral(Context& context, + Parse::NodeId node_id, + SemIR::IntKind int_kind, + IntId size_id) -> bool { + if (!(context.ints().Get(size_id) & 3).isZero()) { + CARBON_DIAGNOSTIC(IntWidthNotMultipleOf8, Error, + "Bit width of integer type literal must be a multiple of " + "8. Use `Core.{0}({1})` instead.", + std::string, llvm::APSInt); + context.emitter().Emit( + node_id, IntWidthNotMultipleOf8, int_kind.is_signed() ? "Int" : "UInt", + llvm::APSInt(context.ints().Get(size_id), /*isUnsigned=*/true)); + } + // TODO: Migrate to a call to `Core.Int` or `Core.UInt`. + auto width_id = MakeI32Literal(context, node_id, size_id); + context.AddInstAndPush( + {node_id, SemIR::IntType{.type_id = context.GetBuiltinType( + SemIR::BuiltinKind::TypeType), + .int_kind = int_kind, + .bit_width_id = width_id}}); + return true; +} + auto HandleIntTypeLiteral(Context& context, Parse::IntTypeLiteralId node_id) -> bool { - auto text = - context.tokens().GetTokenText(context.parse_tree().node_token(node_id)); - if (text != "i32") { - return context.TODO(node_id, "Currently only i32 is allowed"); + auto tok_id = context.parse_tree().node_token(node_id); + auto size_id = context.tokens().GetTypeLiteralSize(tok_id); + // Special case: `i32` has a custom builtin for now. + // TODO: Remove this special case. + if (context.ints().Get(size_id) == 32) { + context.node_stack().Push(node_id, SemIR::InstId::BuiltinIntType); + return true; } - // TODO: Migrate once functions can be in prelude.carbon. - // auto fn_inst_id = context.LookupNameInCore(node_id, "Int32"); - // auto type_inst_id = PerformCall(context, node_id, fn_inst_id, {}); - // context.node_stack().Push(node_id, type_inst_id); - context.node_stack().Push(node_id, SemIR::InstId::BuiltinIntType); - return true; + return HandleIntOrUnsignedIntTypeLiteral(context, node_id, + SemIR::IntKind::Signed, size_id); } auto HandleUnsignedIntTypeLiteral(Context& context, Parse::UnsignedIntTypeLiteralId node_id) -> bool { - return context.TODO(node_id, "Need to support unsigned type literals"); + auto tok_id = context.parse_tree().node_token(node_id); + auto size_id = context.tokens().GetTypeLiteralSize(tok_id); + return HandleIntOrUnsignedIntTypeLiteral(context, node_id, + SemIR::IntKind::Unsigned, size_id); } auto HandleFloatTypeLiteral(Context& context, Parse::FloatTypeLiteralId node_id) diff --git a/toolchain/check/testdata/basics/type_literals.carbon b/toolchain/check/testdata/basics/type_literals.carbon new file mode 100644 index 000000000000..b0875ced0c9d --- /dev/null +++ b/toolchain/check/testdata/basics/type_literals.carbon @@ -0,0 +1,263 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- iN.carbon +library "iN" api; + +var test_i8: i8; +var test_i16: i16; +var test_i64: i64; + +// --- fail_iN_bad_width.carbon +library "fail_iN_bad_width" api; + +// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Name `i0` not found. +// CHECK:STDERR: var test_i0: i0; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_i0: i0; +// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Bit width of integer type literal must be a multiple of 8. Use `Core.Int(1)` instead. +// CHECK:STDERR: var test_i1: i1; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_i1: i1; +// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:15: ERROR: Bit width of integer type literal must be a multiple of 8. Use `Core.Int(15)` instead. +// CHECK:STDERR: var test_i15: i15; +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +var test_i15: i15; +// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:23: ERROR: Integer type width of 1000000000 is greater than the maximum supported width of 8388608. +// CHECK:STDERR: var test_i1000000000: i1000000000; +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: +var test_i1000000000: i1000000000; +// TODO: This diagnostic is not very good. +// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:33: ERROR: Integer literal with value 10000000000000000000 does not fit in i32. +// CHECK:STDERR: var test_i10000000000000000000: i10000000000000000000; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var test_i10000000000000000000: i10000000000000000000; + +// --- uN.carbon +library "uN" api; + +var test_u8: u8; +var test_u16: u16; +var test_u64: u64; + +// --- fail_uN_bad_width.carbon +library "fail_uN_bad_width" api; + +// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Name `u0` not found. +// CHECK:STDERR: var test_u0: u0; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_u0: u0; +// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Bit width of integer type literal must be a multiple of 8. Use `Core.UInt(1)` instead. +// CHECK:STDERR: var test_u1: u1; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_u1: u1; +// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:15: ERROR: Bit width of integer type literal must be a multiple of 8. Use `Core.UInt(15)` instead. +// CHECK:STDERR: var test_u15: u15; +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +var test_u15: u15; +// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:23: ERROR: Integer type width of 1000000000 is greater than the maximum supported width of 8388608. +// CHECK:STDERR: var test_u1000000000: u1000000000; +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: +var test_u1000000000: u1000000000; +// TODO: This diagnostic is not very good. +// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:33: ERROR: Integer literal with value 10000000000000000000 does not fit in i32. +// CHECK:STDERR: var test_u10000000000000000000: u10000000000000000000; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var test_u10000000000000000000: u10000000000000000000; + +// --- fail_fN_bad_width.carbon +library "fail_fN_bad_width" api; + +// CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Name `f0` not found. +// CHECK:STDERR: var test_f0: f0; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_f0: f0; +// CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:14: ERROR: Semantics TODO: `Currently only f64 is allowed`. +// CHECK:STDERR: var test_f1: f1; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var test_f1: f1; +var test_f15: f15; +var test_f100: f100; +var test_f1000000000: f1000000000; +var test_f1000000000000: f1000000000000; + +// --- fail_fN_todo_unsupported.carbon +library "fail_fN_todo_unsupported" api; + +// TODO: Some or all of these should eventually work. +// CHECK:STDERR: fail_fN_todo_unsupported.carbon:[[@LINE+3]]:15: ERROR: Semantics TODO: `Currently only f64 is allowed`. +// CHECK:STDERR: var test_f16: f16; +// CHECK:STDERR: ^~~ +var test_f16: f16; +var test_f32: f32; +var test_f128: f128; + +// CHECK:STDOUT: --- iN.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 8 [template] +// CHECK:STDOUT: %.2: type = int_type signed, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 16 [template] +// CHECK:STDOUT: %.4: type = int_type signed, %.3 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 64 [template] +// CHECK:STDOUT: %.6: type = int_type signed, %.5 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .test_i8 = %test_i8 +// CHECK:STDOUT: .test_i16 = %test_i16 +// CHECK:STDOUT: .test_i64 = %test_i64 +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %.loc3_14.1: i32 = int_literal 8 [template = constants.%.1] +// CHECK:STDOUT: %.loc3_14.2: type = int_type signed, %.loc3_14.1 [template = constants.%.2] +// CHECK:STDOUT: %test_i8.var: ref i8 = var test_i8 +// CHECK:STDOUT: %test_i8: ref i8 = bind_name test_i8, %test_i8.var +// CHECK:STDOUT: %.loc4_15.1: i32 = int_literal 16 [template = constants.%.3] +// CHECK:STDOUT: %.loc4_15.2: type = int_type signed, %.loc4_15.1 [template = constants.%.4] +// CHECK:STDOUT: %test_i16.var: ref i16 = var test_i16 +// CHECK:STDOUT: %test_i16: ref i16 = bind_name test_i16, %test_i16.var +// CHECK:STDOUT: %.loc5_15.1: i32 = int_literal 64 [template = constants.%.5] +// CHECK:STDOUT: %.loc5_15.2: type = int_type signed, %.loc5_15.1 [template = constants.%.6] +// CHECK:STDOUT: %test_i64.var: ref i64 = var test_i64 +// CHECK:STDOUT: %test_i64: ref i64 = bind_name test_i64, %test_i64.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_iN_bad_width.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: type = int_type signed, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 15 [template] +// CHECK:STDOUT: %.4: type = int_type signed, %.3 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 1000000000 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .test_i0 = %test_i0 +// CHECK:STDOUT: .test_i1 = %test_i1 +// CHECK:STDOUT: .test_i15 = %test_i15 +// CHECK:STDOUT: .test_i1000000000 = %test_i1000000000 +// CHECK:STDOUT: .test_i10000000000000000000 = %test_i10000000000000000000 +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %i0.ref: = name_ref i0, [template = ] +// CHECK:STDOUT: %test_i0.var: ref = var test_i0 +// CHECK:STDOUT: %test_i0: ref = bind_name test_i0, %test_i0.var +// CHECK:STDOUT: %.loc12_14.1: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_14.2: type = int_type signed, %.loc12_14.1 [template = constants.%.2] +// CHECK:STDOUT: %test_i1.var: ref i1 = var test_i1 +// CHECK:STDOUT: %test_i1: ref i1 = bind_name test_i1, %test_i1.var +// CHECK:STDOUT: %.loc17_15.1: i32 = int_literal 15 [template = constants.%.3] +// CHECK:STDOUT: %.loc17_15.2: type = int_type signed, %.loc17_15.1 [template = constants.%.4] +// CHECK:STDOUT: %test_i15.var: ref i15 = var test_i15 +// CHECK:STDOUT: %test_i15: ref i15 = bind_name test_i15, %test_i15.var +// CHECK:STDOUT: %.loc22_23.1: i32 = int_literal 1000000000 [template = constants.%.5] +// CHECK:STDOUT: %.loc22_23.2: type = int_type signed, %.loc22_23.1 [template = ] +// CHECK:STDOUT: %test_i1000000000.var: ref = var test_i1000000000 +// CHECK:STDOUT: %test_i1000000000: ref = bind_name test_i1000000000, %test_i1000000000.var +// CHECK:STDOUT: %.loc28: type = int_type signed, [template = ] +// CHECK:STDOUT: %test_i10000000000000000000.var: ref = var test_i10000000000000000000 +// CHECK:STDOUT: %test_i10000000000000000000: ref = bind_name test_i10000000000000000000, %test_i10000000000000000000.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- uN.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 8 [template] +// CHECK:STDOUT: %.2: type = int_type unsigned, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 16 [template] +// CHECK:STDOUT: %.4: type = int_type unsigned, %.3 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 64 [template] +// CHECK:STDOUT: %.6: type = int_type unsigned, %.5 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .test_u8 = %test_u8 +// CHECK:STDOUT: .test_u16 = %test_u16 +// CHECK:STDOUT: .test_u64 = %test_u64 +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %.loc3_14.1: i32 = int_literal 8 [template = constants.%.1] +// CHECK:STDOUT: %.loc3_14.2: type = int_type unsigned, %.loc3_14.1 [template = constants.%.2] +// CHECK:STDOUT: %test_u8.var: ref u8 = var test_u8 +// CHECK:STDOUT: %test_u8: ref u8 = bind_name test_u8, %test_u8.var +// CHECK:STDOUT: %.loc4_15.1: i32 = int_literal 16 [template = constants.%.3] +// CHECK:STDOUT: %.loc4_15.2: type = int_type unsigned, %.loc4_15.1 [template = constants.%.4] +// CHECK:STDOUT: %test_u16.var: ref u16 = var test_u16 +// CHECK:STDOUT: %test_u16: ref u16 = bind_name test_u16, %test_u16.var +// CHECK:STDOUT: %.loc5_15.1: i32 = int_literal 64 [template = constants.%.5] +// CHECK:STDOUT: %.loc5_15.2: type = int_type unsigned, %.loc5_15.1 [template = constants.%.6] +// CHECK:STDOUT: %test_u64.var: ref u64 = var test_u64 +// CHECK:STDOUT: %test_u64: ref u64 = bind_name test_u64, %test_u64.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_uN_bad_width.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: type = int_type unsigned, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 15 [template] +// CHECK:STDOUT: %.4: type = int_type unsigned, %.3 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 1000000000 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .test_u0 = %test_u0 +// CHECK:STDOUT: .test_u1 = %test_u1 +// CHECK:STDOUT: .test_u15 = %test_u15 +// CHECK:STDOUT: .test_u1000000000 = %test_u1000000000 +// CHECK:STDOUT: .test_u10000000000000000000 = %test_u10000000000000000000 +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %u0.ref: = name_ref u0, [template = ] +// CHECK:STDOUT: %test_u0.var: ref = var test_u0 +// CHECK:STDOUT: %test_u0: ref = bind_name test_u0, %test_u0.var +// CHECK:STDOUT: %.loc12_14.1: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_14.2: type = int_type unsigned, %.loc12_14.1 [template = constants.%.2] +// CHECK:STDOUT: %test_u1.var: ref u1 = var test_u1 +// CHECK:STDOUT: %test_u1: ref u1 = bind_name test_u1, %test_u1.var +// CHECK:STDOUT: %.loc17_15.1: i32 = int_literal 15 [template = constants.%.3] +// CHECK:STDOUT: %.loc17_15.2: type = int_type unsigned, %.loc17_15.1 [template = constants.%.4] +// CHECK:STDOUT: %test_u15.var: ref u15 = var test_u15 +// CHECK:STDOUT: %test_u15: ref u15 = bind_name test_u15, %test_u15.var +// CHECK:STDOUT: %.loc22_23.1: i32 = int_literal 1000000000 [template = constants.%.5] +// CHECK:STDOUT: %.loc22_23.2: type = int_type unsigned, %.loc22_23.1 [template = ] +// CHECK:STDOUT: %test_u1000000000.var: ref = var test_u1000000000 +// CHECK:STDOUT: %test_u1000000000: ref = bind_name test_u1000000000, %test_u1000000000.var +// CHECK:STDOUT: %.loc28: type = int_type unsigned, [template = ] +// CHECK:STDOUT: %test_u10000000000000000000.var: ref = var test_u10000000000000000000 +// CHECK:STDOUT: %test_u10000000000000000000: ref = bind_name test_u10000000000000000000, %test_u10000000000000000000.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_fN_bad_width.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_fN_todo_unsupported.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int/make_type_signed.carbon b/toolchain/check/testdata/builtins/int/make_type_signed.carbon new file mode 100644 index 000000000000..15e533ef2855 --- /dev/null +++ b/toolchain/check/testdata/builtins/int/make_type_signed.carbon @@ -0,0 +1,261 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- types.carbon + +library "types" api; + +fn Int(n: i32) -> type = "int.make_type_signed"; + +// --- use_types.carbon + +library "uses_types" api; + +import library "types"; + +fn F(n: Int(64)) -> Int(64) { + return n; +} + +fn G(n: Int(13)) -> Int(13) { + return n; +} + +fn Symbolic(N:! i32, x: Int(N)) -> Int(N) { + return x; +} + +// --- fail_zero_size.carbon + +library "fail_zero_size" api; + +import library "types"; + +// CHECK:STDERR: fail_zero_size.carbon:[[@LINE+4]]:8: ERROR: Integer type width of 0 is not positive. +// CHECK:STDERR: var n: Int(0); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +var n: Int(0); + +// --- fail_negative_size.carbon + +library "fail_negative_size" api; + +import library "types"; + +fn Negate(n: i32) -> i32 = "int.negate"; + +// CHECK:STDERR: fail_negative_size.carbon:[[@LINE+4]]:8: ERROR: Integer type width of -1 is not positive. +// CHECK:STDERR: var n: Int(Negate(1)); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +var n: Int(Negate(1)); + +// --- fail_oversized.carbon + +library "fail_oversized" api; + +import library "types"; + +// CHECK:STDERR: fail_oversized.carbon:[[@LINE+3]]:8: ERROR: Integer type width of 1000000000 is greater than the maximum supported width of 8388608. +// CHECK:STDERR: var m: Int(1000000000); +// CHECK:STDERR: ^~~~ +var m: Int(1000000000); + +// CHECK:STDOUT: --- types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Int = %Int +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Int: = fn_decl @Int [template] { +// CHECK:STDOUT: %n.loc4_8.1: i32 = param n +// CHECK:STDOUT: @Int.%n: i32 = bind_name n, %n.loc4_8.1 +// CHECK:STDOUT: %return.var: ref type = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int(%n: i32) -> type = "int.make_type_signed"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 64 [template] +// CHECK:STDOUT: %.2: type = int_type signed, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 13 [template] +// CHECK:STDOUT: %.4: type = int_type signed, %.3 [template] +// CHECK:STDOUT: %.5: type = int_type signed, @Symbolic.%N [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Int = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: .Symbolic = %Symbolic +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_13 [template = imports.%Int] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %F: = fn_decl @F [template] { +// CHECK:STDOUT: %Int.ref.loc6_9: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc6_13: i32 = int_literal 64 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_12.1: init type = call %Int.ref.loc6_9(%.loc6_13) [template = constants.%.2] +// CHECK:STDOUT: %.loc6_15: type = value_of_initializer %.loc6_12.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_12.2: type = converted %.loc6_12.1, %.loc6_15 [template = constants.%.2] +// CHECK:STDOUT: %n.loc6_6.1: i64 = param n +// CHECK:STDOUT: @F.%n: i64 = bind_name n, %n.loc6_6.1 +// CHECK:STDOUT: %Int.ref.loc6_21: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc6_25: i32 = int_literal 64 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_24.1: init type = call %Int.ref.loc6_21(%.loc6_25) [template = constants.%.2] +// CHECK:STDOUT: %.loc6_27: type = value_of_initializer %.loc6_24.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_24.2: type = converted %.loc6_24.1, %.loc6_27 [template = constants.%.2] +// CHECK:STDOUT: %return.var.loc6: ref i64 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %Int.ref.loc10_9: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc10_13: i32 = int_literal 13 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_12.1: init type = call %Int.ref.loc10_9(%.loc10_13) [template = constants.%.4] +// CHECK:STDOUT: %.loc10_15: type = value_of_initializer %.loc10_12.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_12.2: type = converted %.loc10_12.1, %.loc10_15 [template = constants.%.4] +// CHECK:STDOUT: %n.loc10_6.1: i13 = param n +// CHECK:STDOUT: @G.%n: i13 = bind_name n, %n.loc10_6.1 +// CHECK:STDOUT: %Int.ref.loc10_21: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc10_25: i32 = int_literal 13 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_24.1: init type = call %Int.ref.loc10_21(%.loc10_25) [template = constants.%.4] +// CHECK:STDOUT: %.loc10_27: type = value_of_initializer %.loc10_24.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_24.2: type = converted %.loc10_24.1, %.loc10_27 [template = constants.%.4] +// CHECK:STDOUT: %return.var.loc10: ref i13 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Symbolic: = fn_decl @Symbolic [template] { +// CHECK:STDOUT: %N.loc14_13.1: i32 = param N +// CHECK:STDOUT: @Symbolic.%N: i32 = bind_symbolic_name N, %N.loc14_13.1 [symbolic] +// CHECK:STDOUT: %Int.ref.loc14_25: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %N.ref.loc14_29: i32 = name_ref N, @Symbolic.%N [symbolic = @Symbolic.%N] +// CHECK:STDOUT: %.loc14_28.1: init type = call %Int.ref.loc14_25(%N.ref.loc14_29) [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_30: type = value_of_initializer %.loc14_28.1 [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_28.2: type = converted %.loc14_28.1, %.loc14_30 [symbolic = constants.%.5] +// CHECK:STDOUT: %x.loc14_22.1: Core.Int(N) = param x +// CHECK:STDOUT: @Symbolic.%x: Core.Int(N) = bind_name x, %x.loc14_22.1 +// CHECK:STDOUT: %Int.ref.loc14_36: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %N.ref.loc14_40: i32 = name_ref N, @Symbolic.%N [symbolic = @Symbolic.%N] +// CHECK:STDOUT: %.loc14_39.1: init type = call %Int.ref.loc14_36(%N.ref.loc14_40) [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_41: type = value_of_initializer %.loc14_39.1 [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_39.2: type = converted %.loc14_39.1, %.loc14_41 [symbolic = constants.%.5] +// CHECK:STDOUT: %return.var.loc14: ref Core.Int(N) = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int(%n: i32) -> type = "int.make_type_signed"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%n: i64) -> i64 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: i64 = name_ref n, %n +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%n: i13) -> i13 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: i13 = name_ref n, %n +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Symbolic(%N: i32, %x: Core.Int(N)) -> Core.Int(N) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: Core.Int(N) = name_ref x, %x +// CHECK:STDOUT: return %x.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_zero_size.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Int = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .n = %n +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_11 [template = imports.%Int] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Int.ref: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc10_12: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_11.1: init type = call %Int.ref(%.loc10_12) [template = ] +// CHECK:STDOUT: %.loc10_13: type = value_of_initializer %.loc10_11.1 [template = ] +// CHECK:STDOUT: %.loc10_11.2: type = converted %.loc10_11.1, %.loc10_13 [template = ] +// CHECK:STDOUT: %n.var: ref = var n +// CHECK:STDOUT: %n: ref = bind_name n, %n.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int(%n: i32) -> type = "int.make_type_signed"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_negative_size.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal -1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Int = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: .n = %n.loc12 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_23 [template = imports.%Int] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %n.loc6_11.1: i32 = param n +// CHECK:STDOUT: @Negate.%n: i32 = bind_name n, %n.loc6_11.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Int.ref: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %Negate.ref: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc12_19: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_18.1: init i32 = call %Negate.ref(%.loc12_19) [template = constants.%.2] +// CHECK:STDOUT: %.loc12_11.1: i32 = value_of_initializer %.loc12_18.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_18.2: i32 = converted %.loc12_18.1, %.loc12_11.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_11.2: init type = call %Int.ref(%.loc12_18.2) [template = ] +// CHECK:STDOUT: %.loc12_21: type = value_of_initializer %.loc12_11.2 [template = ] +// CHECK:STDOUT: %.loc12_11.3: type = converted %.loc12_11.2, %.loc12_21 [template = ] +// CHECK:STDOUT: %n.var: ref = var n +// CHECK:STDOUT: %n.loc12: ref = bind_name n, %n.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%n: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int(%n: i32) -> type = "int.make_type_signed"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_oversized.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1000000000 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Int = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .m = %m +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_11 [template = imports.%Int] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Int.ref: = name_ref Int, %import_ref [template = imports.%Int] +// CHECK:STDOUT: %.loc9_12: i32 = int_literal 1000000000 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_11.1: init type = call %Int.ref(%.loc9_12) [template = ] +// CHECK:STDOUT: %.loc9_22: type = value_of_initializer %.loc9_11.1 [template = ] +// CHECK:STDOUT: %.loc9_11.2: type = converted %.loc9_11.1, %.loc9_22 [template = ] +// CHECK:STDOUT: %m.var: ref = var m +// CHECK:STDOUT: %m: ref = bind_name m, %m.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int(%n: i32) -> type = "int.make_type_signed"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/builtins/int/make_type_unsigned.carbon b/toolchain/check/testdata/builtins/int/make_type_unsigned.carbon new file mode 100644 index 000000000000..275a194ebafe --- /dev/null +++ b/toolchain/check/testdata/builtins/int/make_type_unsigned.carbon @@ -0,0 +1,261 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- types.carbon + +library "types" api; + +fn UInt(n: i32) -> type = "int.make_type_unsigned"; + +// --- use_types.carbon + +library "uses_types" api; + +import library "types"; + +fn F(n: UInt(64)) -> UInt(64) { + return n; +} + +fn G(n: UInt(13)) -> UInt(13) { + return n; +} + +fn Symbolic(N:! i32, x: UInt(N)) -> UInt(N) { + return x; +} + +// --- fail_zero_size.carbon + +library "fail_zero_size" api; + +import library "types"; + +// CHECK:STDERR: fail_zero_size.carbon:[[@LINE+4]]:8: ERROR: Integer type width of 0 is not positive. +// CHECK:STDERR: var n: UInt(0); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var n: UInt(0); + +// --- fail_negative_size.carbon + +library "fail_negative_size" api; + +import library "types"; + +fn Negate(n: i32) -> i32 = "int.negate"; + +// CHECK:STDERR: fail_negative_size.carbon:[[@LINE+4]]:8: ERROR: Integer type width of -1 is not positive. +// CHECK:STDERR: var n: UInt(Negate(1)); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var n: UInt(Negate(1)); + +// --- fail_oversized.carbon + +library "fail_oversized" api; + +import library "types"; + +// CHECK:STDERR: fail_oversized.carbon:[[@LINE+3]]:8: ERROR: Integer type width of 1000000000 is greater than the maximum supported width of 8388608. +// CHECK:STDERR: var m: UInt(1000000000); +// CHECK:STDERR: ^~~~~ +var m: UInt(1000000000); + +// CHECK:STDOUT: --- types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .UInt = %UInt +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %UInt: = fn_decl @UInt [template] { +// CHECK:STDOUT: %n.loc4_9.1: i32 = param n +// CHECK:STDOUT: @UInt.%n: i32 = bind_name n, %n.loc4_9.1 +// CHECK:STDOUT: %return.var: ref type = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UInt(%n: i32) -> type = "int.make_type_unsigned"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 64 [template] +// CHECK:STDOUT: %.2: type = int_type unsigned, %.1 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 13 [template] +// CHECK:STDOUT: %.4: type = int_type unsigned, %.3 [template] +// CHECK:STDOUT: %.5: type = int_type unsigned, @Symbolic.%N [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UInt = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: .Symbolic = %Symbolic +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_13 [template = imports.%UInt] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %F: = fn_decl @F [template] { +// CHECK:STDOUT: %UInt.ref.loc6_9: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc6_14: i32 = int_literal 64 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_13.1: init type = call %UInt.ref.loc6_9(%.loc6_14) [template = constants.%.2] +// CHECK:STDOUT: %.loc6_16: type = value_of_initializer %.loc6_13.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_13.2: type = converted %.loc6_13.1, %.loc6_16 [template = constants.%.2] +// CHECK:STDOUT: %n.loc6_6.1: u64 = param n +// CHECK:STDOUT: @F.%n: u64 = bind_name n, %n.loc6_6.1 +// CHECK:STDOUT: %UInt.ref.loc6_22: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc6_27: i32 = int_literal 64 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_26.1: init type = call %UInt.ref.loc6_22(%.loc6_27) [template = constants.%.2] +// CHECK:STDOUT: %.loc6_29: type = value_of_initializer %.loc6_26.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_26.2: type = converted %.loc6_26.1, %.loc6_29 [template = constants.%.2] +// CHECK:STDOUT: %return.var.loc6: ref u64 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %UInt.ref.loc10_9: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc10_14: i32 = int_literal 13 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_13.1: init type = call %UInt.ref.loc10_9(%.loc10_14) [template = constants.%.4] +// CHECK:STDOUT: %.loc10_16: type = value_of_initializer %.loc10_13.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_13.2: type = converted %.loc10_13.1, %.loc10_16 [template = constants.%.4] +// CHECK:STDOUT: %n.loc10_6.1: u13 = param n +// CHECK:STDOUT: @G.%n: u13 = bind_name n, %n.loc10_6.1 +// CHECK:STDOUT: %UInt.ref.loc10_22: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc10_27: i32 = int_literal 13 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_26.1: init type = call %UInt.ref.loc10_22(%.loc10_27) [template = constants.%.4] +// CHECK:STDOUT: %.loc10_29: type = value_of_initializer %.loc10_26.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_26.2: type = converted %.loc10_26.1, %.loc10_29 [template = constants.%.4] +// CHECK:STDOUT: %return.var.loc10: ref u13 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Symbolic: = fn_decl @Symbolic [template] { +// CHECK:STDOUT: %N.loc14_13.1: i32 = param N +// CHECK:STDOUT: @Symbolic.%N: i32 = bind_symbolic_name N, %N.loc14_13.1 [symbolic] +// CHECK:STDOUT: %UInt.ref.loc14_25: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %N.ref.loc14_30: i32 = name_ref N, @Symbolic.%N [symbolic = @Symbolic.%N] +// CHECK:STDOUT: %.loc14_29.1: init type = call %UInt.ref.loc14_25(%N.ref.loc14_30) [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_31: type = value_of_initializer %.loc14_29.1 [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_29.2: type = converted %.loc14_29.1, %.loc14_31 [symbolic = constants.%.5] +// CHECK:STDOUT: %x.loc14_22.1: Core.UInt(N) = param x +// CHECK:STDOUT: @Symbolic.%x: Core.UInt(N) = bind_name x, %x.loc14_22.1 +// CHECK:STDOUT: %UInt.ref.loc14_37: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %N.ref.loc14_42: i32 = name_ref N, @Symbolic.%N [symbolic = @Symbolic.%N] +// CHECK:STDOUT: %.loc14_41.1: init type = call %UInt.ref.loc14_37(%N.ref.loc14_42) [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_43: type = value_of_initializer %.loc14_41.1 [symbolic = constants.%.5] +// CHECK:STDOUT: %.loc14_41.2: type = converted %.loc14_41.1, %.loc14_43 [symbolic = constants.%.5] +// CHECK:STDOUT: %return.var.loc14: ref Core.UInt(N) = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UInt(%n: i32) -> type = "int.make_type_unsigned"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%n: u64) -> u64 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: u64 = name_ref n, %n +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%n: u13) -> u13 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: u13 = name_ref n, %n +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Symbolic(%N: i32, %x: Core.UInt(N)) -> Core.UInt(N) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: Core.UInt(N) = name_ref x, %x +// CHECK:STDOUT: return %x.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_zero_size.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UInt = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .n = %n +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_11 [template = imports.%UInt] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %UInt.ref: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc10_13: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_12.1: init type = call %UInt.ref(%.loc10_13) [template = ] +// CHECK:STDOUT: %.loc10_14: type = value_of_initializer %.loc10_12.1 [template = ] +// CHECK:STDOUT: %.loc10_12.2: type = converted %.loc10_12.1, %.loc10_14 [template = ] +// CHECK:STDOUT: %n.var: ref = var n +// CHECK:STDOUT: %n: ref = bind_name n, %n.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UInt(%n: i32) -> type = "int.make_type_unsigned"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_negative_size.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal -1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UInt = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Negate = %Negate +// CHECK:STDOUT: .n = %n.loc12 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_23 [template = imports.%UInt] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Negate: = fn_decl @Negate [template] { +// CHECK:STDOUT: %n.loc6_11.1: i32 = param n +// CHECK:STDOUT: @Negate.%n: i32 = bind_name n, %n.loc6_11.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %UInt.ref: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %Negate.ref: = name_ref Negate, %Negate [template = %Negate] +// CHECK:STDOUT: %.loc12_20: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_19.1: init i32 = call %Negate.ref(%.loc12_20) [template = constants.%.2] +// CHECK:STDOUT: %.loc12_12.1: i32 = value_of_initializer %.loc12_19.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_19.2: i32 = converted %.loc12_19.1, %.loc12_12.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc12_12.2: init type = call %UInt.ref(%.loc12_19.2) [template = ] +// CHECK:STDOUT: %.loc12_22: type = value_of_initializer %.loc12_12.2 [template = ] +// CHECK:STDOUT: %.loc12_12.3: type = converted %.loc12_12.2, %.loc12_22 [template = ] +// CHECK:STDOUT: %n.var: ref = var n +// CHECK:STDOUT: %n.loc12: ref = bind_name n, %n.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Negate(%n: i32) -> i32 = "int.negate"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UInt(%n: i32) -> type = "int.make_type_unsigned"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_oversized.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1000000000 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UInt = %import_ref +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .m = %m +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref: = import_ref ir2, inst+5, loc_11 [template = imports.%UInt] +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %UInt.ref: = name_ref UInt, %import_ref [template = imports.%UInt] +// CHECK:STDOUT: %.loc9_13: i32 = int_literal 1000000000 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_12.1: init type = call %UInt.ref(%.loc9_13) [template = ] +// CHECK:STDOUT: %.loc9_23: type = value_of_initializer %.loc9_12.1 [template = ] +// CHECK:STDOUT: %.loc9_12.2: type = converted %.loc9_12.1, %.loc9_23 [template = ] +// CHECK:STDOUT: %m.var: ref = var m +// CHECK:STDOUT: %m: ref = bind_name m, %m.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UInt(%n: i32) -> type = "int.make_type_unsigned"; +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 6503a82ae9ef..9fe6be0fff8e 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -270,6 +270,9 @@ CARBON_DIAGNOSTIC_KIND(IncompleteTypeInMemberAccess) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInValueConversion) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInVarDecl) CARBON_DIAGNOSTIC_KIND(IntLiteralTooLargeForI32) +CARBON_DIAGNOSTIC_KIND(IntWidthNotMultipleOf8) +CARBON_DIAGNOSTIC_KIND(IntWidthNotPositive) +CARBON_DIAGNOSTIC_KIND(IntWidthTooLarge) CARBON_DIAGNOSTIC_KIND(InvalidArrayExpr) CARBON_DIAGNOSTIC_KIND(TypeNotIndexable) CARBON_DIAGNOSTIC_KIND(SelfOutsideImplicitParamList) diff --git a/toolchain/lex/tokenized_buffer.cpp b/toolchain/lex/tokenized_buffer.cpp index 680ef4499791..e9b18f22e09c 100644 --- a/toolchain/lex/tokenized_buffer.cpp +++ b/toolchain/lex/tokenized_buffer.cpp @@ -134,11 +134,10 @@ auto TokenizedBuffer::GetStringLiteralValue(TokenIndex token) const return token_info.string_literal_id; } -auto TokenizedBuffer::GetTypeLiteralSize(TokenIndex token) const - -> const llvm::APInt& { +auto TokenizedBuffer::GetTypeLiteralSize(TokenIndex token) const -> IntId { const auto& token_info = GetTokenInfo(token); CARBON_CHECK(token_info.kind.is_sized_type_literal()) << token_info.kind; - return value_stores_->ints().Get(token_info.int_id); + return token_info.int_id; } auto TokenizedBuffer::GetMatchedClosingToken(TokenIndex opening_token) const diff --git a/toolchain/lex/tokenized_buffer.h b/toolchain/lex/tokenized_buffer.h index 9d92748ec07d..ae43a201fbbd 100644 --- a/toolchain/lex/tokenized_buffer.h +++ b/toolchain/lex/tokenized_buffer.h @@ -166,7 +166,7 @@ class TokenizedBuffer : public Printable { auto GetStringLiteralValue(TokenIndex token) const -> StringLiteralValueId; // Returns the size specified in a `*TypeLiteral()` token. - auto GetTypeLiteralSize(TokenIndex token) const -> const llvm::APInt&; + auto GetTypeLiteralSize(TokenIndex token) const -> IntId; // Returns the closing token matched with the given opening token. // diff --git a/toolchain/lex/tokenized_buffer_test.cpp b/toolchain/lex/tokenized_buffer_test.cpp index b18d6b9653cd..0b4519716dfd 100644 --- a/toolchain/lex/tokenized_buffer_test.cpp +++ b/toolchain/lex/tokenized_buffer_test.cpp @@ -941,22 +941,19 @@ TEST_F(LexerTest, TypeLiterals) { {.kind = TokenKind::FileEnd, .line = 6, .column = 3}, })); - auto token_i1 = buffer.tokens().begin() + 2; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_i1), 1); - auto token_i20 = buffer.tokens().begin() + 3; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_i20), 20); - auto token_i999999999999 = buffer.tokens().begin() + 4; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_i999999999999), 999999999999ULL); - auto token_u1 = buffer.tokens().begin() + 7; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_u1), 1); - auto token_u64 = buffer.tokens().begin() + 8; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_u64), 64); - auto token_f32 = buffer.tokens().begin() + 10; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_f32), 32); - auto token_f80 = buffer.tokens().begin() + 11; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_f80), 80); - auto token_f1 = buffer.tokens().begin() + 12; - EXPECT_EQ(buffer.GetTypeLiteralSize(*token_f1), 1); + auto type_size = [&](int token_index) { + auto token = buffer.tokens().begin()[token_index]; + return value_stores_.ints().Get(buffer.GetTypeLiteralSize(token)); + }; + + EXPECT_EQ(type_size(2), 1); + EXPECT_EQ(type_size(3), 20); + EXPECT_EQ(type_size(4), 999999999999ULL); + EXPECT_EQ(type_size(7), 1); + EXPECT_EQ(type_size(8), 64); + EXPECT_EQ(type_size(10), 32); + EXPECT_EQ(type_size(11), 80); + EXPECT_EQ(type_size(12), 1); } TEST_F(LexerTest, TypeLiteralTooManyDigits) { diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index ba700247e58f..18f1fd569ffe 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -319,6 +319,13 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { // Return an empty struct as a placeholder. // TODO: Should we model an interface as a witness table? return llvm::StructType::get(*llvm_context_); + case SemIR::IntType::Kind: { + auto width = sem_ir_->insts().TryGetAs( + inst.As().bit_width_id); + CARBON_CHECK(width) << "Can't lower int type with symbolic width"; + return llvm::IntegerType::get( + *llvm_context_, sem_ir_->ints().Get(width->int_id).getZExtValue()); + } case SemIR::PointerType::Kind: return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0); case SemIR::StructType::Kind: { diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index b4d13a93bb31..437098b1de57 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -212,6 +212,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::BoolMakeType: case SemIR::BuiltinFunctionKind::FloatMakeType: case SemIR::BuiltinFunctionKind::IntMakeType32: + case SemIR::BuiltinFunctionKind::IntMakeTypeSigned: + case SemIR::BuiltinFunctionKind::IntMakeTypeUnsigned: context.SetLocal(inst_id, context.GetTypeAsValue()); return; @@ -260,15 +262,23 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, return; } case SemIR::BuiltinFunctionKind::IntDiv: { - context.SetLocal(inst_id, context.builder().CreateSDiv( - context.GetValue(arg_ids[0]), - context.GetValue(arg_ids[1]), "div")); + context.SetLocal(inst_id, IsSignedInt(context, inst_id) + ? context.builder().CreateSDiv( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "div") + : context.builder().CreateUDiv( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "div")); return; } case SemIR::BuiltinFunctionKind::IntMod: { - context.SetLocal(inst_id, context.builder().CreateSRem( - context.GetValue(arg_ids[0]), - context.GetValue(arg_ids[1]), "rem")); + context.SetLocal(inst_id, IsSignedInt(context, inst_id) + ? context.builder().CreateSRem( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "rem") + : context.builder().CreateURem( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "rem")); return; } case SemIR::BuiltinFunctionKind::IntAnd: { diff --git a/toolchain/lower/handle_type.cpp b/toolchain/lower/handle_type.cpp index 04df5fddc1fb..0a10f7155bed 100644 --- a/toolchain/lower/handle_type.cpp +++ b/toolchain/lower/handle_type.cpp @@ -37,6 +37,11 @@ auto HandleInterfaceType(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetTypeAsValue()); } +auto HandleIntType(FunctionContext& context, SemIR::InstId inst_id, + SemIR::IntType /*inst*/) -> void { + context.SetLocal(inst_id, context.GetTypeAsValue()); +} + auto HandlePointerType(FunctionContext& context, SemIR::InstId inst_id, SemIR::PointerType /*inst*/) -> void { context.SetLocal(inst_id, context.GetTypeAsValue()); diff --git a/toolchain/lower/testdata/basics/int_types.carbon b/toolchain/lower/testdata/basics/int_types.carbon new file mode 100644 index 000000000000..577d4695cb76 --- /dev/null +++ b/toolchain/lower/testdata/basics/int_types.carbon @@ -0,0 +1,29 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F_i8(a: i8) -> i8 { return a; } +fn F_u16(a: u16) -> u16 { return a; } +fn F_i64(a: i64) -> i64 { return a; } +fn F_u65536(a: u65536) -> u65536 { return a; } + +// CHECK:STDOUT: ; ModuleID = 'int_types.carbon' +// CHECK:STDOUT: source_filename = "int_types.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i8 @F_i8(i8 %a) { +// CHECK:STDOUT: ret i8 %a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i16 @F_u16(i16 %a) { +// CHECK:STDOUT: ret i16 %a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @F_i64(i64 %a) { +// CHECK:STDOUT: ret i64 %a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i65536 @F_u65536(i65536 %a) { +// CHECK:STDOUT: ret i65536 %a +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/basics/type_values.carbon b/toolchain/lower/testdata/basics/type_values.carbon index 6e93c667792b..67f7b194e07c 100644 --- a/toolchain/lower/testdata/basics/type_values.carbon +++ b/toolchain/lower/testdata/basics/type_values.carbon @@ -11,6 +11,10 @@ fn I32() -> type { return i32; } +fn I48() -> type { + return i48; +} + fn F64() -> type { return f64; } @@ -24,6 +28,10 @@ fn F64() -> type { // CHECK:STDOUT: ret %type zeroinitializer // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: define %type @I48() { +// CHECK:STDOUT: ret %type zeroinitializer +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: define %type @F64() { // CHECK:STDOUT: ret %type zeroinitializer // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/builtins/uint.carbon b/toolchain/lower/testdata/builtins/uint.carbon new file mode 100644 index 000000000000..0be02e87cc7f --- /dev/null +++ b/toolchain/lower/testdata/builtins/uint.carbon @@ -0,0 +1,152 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn Negate(a: u64) -> u64 = "int.negate"; +fn TestNegate(a: u64) -> u64 { return Negate(a); } + +fn Add(a: u64, b: u64) -> u64 = "int.add"; +fn TestAdd(a: u64, b: u64) -> u64 { return Add(a, b); } + +fn Sub(a: u64, b: u64) -> u64 = "int.sub"; +fn TestSub(a: u64, b: u64) -> u64 { return Sub(a, b); } + +fn Mul(a: u64, b: u64) -> u64 = "int.mul"; +fn TestMul(a: u64, b: u64) -> u64 { return Mul(a, b); } + +fn Div(a: u64, b: u64) -> u64 = "int.div"; +fn TestDiv(a: u64, b: u64) -> u64 { return Div(a, b); } + +fn Mod(a: u64, b: u64) -> u64 = "int.mod"; +fn TestMod(a: u64, b: u64) -> u64 { return Mod(a, b); } + +fn Complement(a: u64) -> u64 = "int.complement"; +fn TestComplement(a: u64) -> u64 { return Complement(a); } + +fn And(a: u64, b: u64) -> u64 = "int.and"; +fn TestAnd(a: u64, b: u64) -> u64 { return And(a, b); } + +fn Or(a: u64, b: u64) -> u64 = "int.or"; +fn TestOr(a: u64, b: u64) -> u64 { return Or(a, b); } + +fn Xor(a: u64, b: u64) -> u64 = "int.xor"; +fn TestXor(a: u64, b: u64) -> u64 { return Xor(a, b); } + +fn LeftShift(a: u64, b: u64) -> u64 = "int.left_shift"; +fn TestLeftShift(a: u64, b: u64) -> u64 { return LeftShift(a, b); } + +fn RightShift(a: u64, b: u64) -> u64 = "int.right_shift"; +fn TestRightShift(a: u64, b: u64) -> u64 { return RightShift(a, b); } + +fn Eq(a: u64, b: u64) -> bool = "int.eq"; +fn TestEq(a: u64, b: u64) -> bool { return Eq(a, b); } + +fn Neq(a: u64, b: u64) -> bool = "int.neq"; +fn TestNeq(a: u64, b: u64) -> bool { return Neq(a, b); } + +fn Less(a: u64, b: u64) -> bool = "int.less"; +fn TestLess(a: u64, b: u64) -> bool { return Less(a, b); } + +fn LessEq(a: u64, b: u64) -> bool = "int.less_eq"; +fn TestLessEq(a: u64, b: u64) -> bool { return LessEq(a, b); } + +fn Greater(a: u64, b: u64) -> bool = "int.greater"; +fn TestGreater(a: u64, b: u64) -> bool { return Greater(a, b); } + +fn GreaterEq(a: u64, b: u64) -> bool = "int.greater_eq"; +fn TestGreaterEq(a: u64, b: u64) -> bool { return GreaterEq(a, b); } + +// CHECK:STDOUT: ; ModuleID = 'uint.carbon' +// CHECK:STDOUT: source_filename = "uint.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestNegate(i64 %a) { +// CHECK:STDOUT: %neg = sub i64 0, %a +// CHECK:STDOUT: ret i64 %neg +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestAdd(i64 %a, i64 %b) { +// CHECK:STDOUT: %add = add i64 %a, %b +// CHECK:STDOUT: ret i64 %add +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestSub(i64 %a, i64 %b) { +// CHECK:STDOUT: %sub = sub i64 %a, %b +// CHECK:STDOUT: ret i64 %sub +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestMul(i64 %a, i64 %b) { +// CHECK:STDOUT: %mul = mul i64 %a, %b +// CHECK:STDOUT: ret i64 %mul +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestDiv(i64 %a, i64 %b) { +// CHECK:STDOUT: %div = udiv i64 %a, %b +// CHECK:STDOUT: ret i64 %div +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestMod(i64 %a, i64 %b) { +// CHECK:STDOUT: %rem = urem i64 %a, %b +// CHECK:STDOUT: ret i64 %rem +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestComplement(i64 %a) { +// CHECK:STDOUT: %cmpl = xor i64 -1, %a +// CHECK:STDOUT: ret i64 %cmpl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestAnd(i64 %a, i64 %b) { +// CHECK:STDOUT: %and = and i64 %a, %b +// CHECK:STDOUT: ret i64 %and +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestOr(i64 %a, i64 %b) { +// CHECK:STDOUT: %or = or i64 %a, %b +// CHECK:STDOUT: ret i64 %or +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestXor(i64 %a, i64 %b) { +// CHECK:STDOUT: %xor = xor i64 %a, %b +// CHECK:STDOUT: ret i64 %xor +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestLeftShift(i64 %a, i64 %b) { +// CHECK:STDOUT: %shl = shl i64 %a, %b +// CHECK:STDOUT: ret i64 %shl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i64 @TestRightShift(i64 %a, i64 %b) { +// CHECK:STDOUT: %shr = lshr i64 %a, %b +// CHECK:STDOUT: ret i64 %shr +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestEq(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp eq i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestNeq(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp ne i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestLess(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp ult i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestLessEq(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp ule i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestGreater(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp ugt i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i1 @TestGreaterEq(i64 %a, i64 %b) { +// CHECK:STDOUT: %cmp = icmp uge i64 %a, %b +// CHECK:STDOUT: ret i1 %cmp +// CHECK:STDOUT: } diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 3d1c173812ce..4896b4078910 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -64,10 +64,16 @@ struct BuiltinType { using Bool = BuiltinType; // Constraint that requires the type to be an integer type. -// -// TODO: This only matches i32 for now. Support iN for all N, and the -// Core.BigInt type we use to implement for integer literals. -using AnyInt = BuiltinType; +struct AnyInt { + static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) + -> bool { + // TODO: Support Core.BigInt once it exists. + if (BuiltinType::Check(sem_ir, state, type_id)) { + return true; + } + return sem_ir.types().Is(type_id); + } +}; // Constraint that requires the type to be the type type. using Type = BuiltinType; @@ -141,9 +147,18 @@ constexpr BuiltinInfo None = {"", nullptr}; constexpr BuiltinInfo IntMakeType32 = {"int.make_type_32", ValidateSignatureType>}; +// Returns the `iN` type. +// TODO: Should we use a more specific type as the type of the bit width? +constexpr BuiltinInfo IntMakeTypeSigned = { + "int.make_type_signed", ValidateSignatureType>}; + +// Returns the `uN` type. +constexpr BuiltinInfo IntMakeTypeUnsigned = { + "int.make_type_unsigned", ValidateSignatureType>}; + // Returns float types, such as `f64`. Currently only supports `f64`. constexpr BuiltinInfo FloatMakeType = {"float.make_type", - ValidateSignatureType>}; + ValidateSignatureType>}; // Returns the `bool` type. constexpr BuiltinInfo BoolMakeType = {"bool.make_type", diff --git a/toolchain/sem_ir/builtin_function_kind.def b/toolchain/sem_ir/builtin_function_kind.def index 28a15e4bab20..cd7adbcd20fe 100644 --- a/toolchain/sem_ir/builtin_function_kind.def +++ b/toolchain/sem_ir/builtin_function_kind.def @@ -20,6 +20,8 @@ CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(None) // Type factories. CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeType32) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeTypeSigned) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeTypeUnsigned) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatMakeType) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolMakeType) diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 586fec01c1f6..b8019910a9f7 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -209,6 +209,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case ImportRefLoaded::Kind: case ImportRefUsed::Kind: case InterfaceType::Kind: + case IntType::Kind: case NameRef::Kind: case StructType::Kind: case TupleType::Kind: @@ -398,6 +399,21 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, out << sem_ir.names().GetFormatted(interface_name_id); break; } + case IntType::Kind: { + auto int_type = inst.As(); + if (step.index == 1) { + out << ")"; + } else if (auto width_value = sem_ir.insts().TryGetAs( + int_type.bit_width_id)) { + out << (int_type.int_kind.is_signed() ? "i" : "u"); + sem_ir.ints().Get(width_value->int_id).print(out, /*isSigned=*/false); + } else { + out << (int_type.int_kind.is_signed() ? "Core.Int(" : "Core.UInt("); + steps.push_back(step.Next()); + push_inst_id(int_type.bit_width_id); + } + break; + } case NameRef::Kind: { out << sem_ir.names().GetFormatted(inst.As().name_id); break; @@ -610,6 +626,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case InterfaceWitness::Kind: case InterfaceWitnessAccess::Kind: case IntLiteral::Kind: + case IntType::Kind: case Param::Kind: case PointerType::Kind: case RealLiteral::Kind: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 8c94c1ba3f8f..f23b6452a835 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1219,6 +1219,8 @@ class Formatter { auto FormatArg(InterfaceId id) -> void { FormatInterfaceName(id); } + auto FormatArg(IntKind k) -> void { k.Print(out_); } + auto FormatArg(ImplId id) -> void { FormatImplName(id); } auto FormatArg(ImportIRId id) -> void { out_ << id; } diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 393dcc10e79b..201d4248395e 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -121,7 +121,7 @@ using IdKind = TypeEnum< IntId, RealId, StringLiteralValueId, // From sem_ir/id.h. InstId, ConstantId, BindNameId, FunctionId, ClassId, InterfaceId, ImplId, - ImportIRId, ImportIRInstId, LocId, BoolValue, NameId, NameScopeId, + ImportIRId, ImportIRInstId, LocId, BoolValue, IntKind, NameId, NameScopeId, InstBlockId, TypeId, TypeBlockId, ElementIndex>; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 5bed337fc185..93553aad073e 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -305,6 +305,33 @@ struct BoolValue : public IdBase, public Printable { constexpr BoolValue BoolValue::False = BoolValue(0); constexpr BoolValue BoolValue::True = BoolValue(1); +// An integer kind value -- either "signed" or "unsigned". +// +// This might eventually capture any other properties of an integer type that +// affect its semantics, such as overflow behavior. +struct IntKind : public IdBase, public Printable { + static const IntKind Unsigned; + static const IntKind Signed; + + using IdBase::IdBase; + + // Returns whether this type is signed. + constexpr auto is_signed() -> bool { return *this == Signed; } + + auto Print(llvm::raw_ostream& out) const -> void { + if (*this == Unsigned) { + out << "unsigned"; + } else if (*this == Signed) { + out << "signed"; + } else { + CARBON_FATAL() << "Invalid int kind value " << index; + } + } +}; + +constexpr IntKind IntKind::Unsigned = IntKind(0); +constexpr IntKind IntKind::Signed = IntKind(1); + // The ID of a name. A name is either a string or a special name such as // `self`, `Self`, or `base`. struct NameId : public IdBase, public Printable { diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 4c56e83a01af..2b765480e44e 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -59,6 +59,7 @@ CARBON_SEM_IR_INST_KIND(InterfaceType) CARBON_SEM_IR_INST_KIND(InterfaceWitness) CARBON_SEM_IR_INST_KIND(InterfaceWitnessAccess) CARBON_SEM_IR_INST_KIND(IntLiteral) +CARBON_SEM_IR_INST_KIND(IntType) CARBON_SEM_IR_INST_KIND(NameRef) CARBON_SEM_IR_INST_KIND(Namespace) CARBON_SEM_IR_INST_KIND(Param) diff --git a/toolchain/sem_ir/type.h b/toolchain/sem_ir/type.h index ce0f8ce1563e..05b9c3933f9b 100644 --- a/toolchain/sem_ir/type.h +++ b/toolchain/sem_ir/type.h @@ -85,7 +85,12 @@ class TypeStore : public ValueStore { // Determines whether the given type is a signed integer type. auto IsSignedInt(TypeId int_type_id) const -> bool { - return GetInstId(int_type_id) == InstId::BuiltinIntType; + auto inst_id = GetInstId(int_type_id); + if (inst_id == InstId::BuiltinIntType) { + return true; + } + auto int_type = insts_->TryGetAs(inst_id); + return int_type && int_type->int_kind.is_signed(); } private: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 2308d7718b44..36a5882a2a05 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -567,6 +567,17 @@ struct IntLiteral { IntId int_id; }; +struct IntType { + static constexpr auto Kind = + InstKind::IntType.Define("int_type"); + + TypeId type_id; + IntKind int_kind; + // TODO: Consider adding a more compact way of representing either a small + // unsigned integer bit width or an inst_id. + InstId bit_width_id; +}; + struct NameRef { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind =