Add functions for instantiating builtin types through lookup. (#3857)

Note, my instinct is that `Float(dyn_size)` should be invalid. However,
I think the constant evaluation doesn't result in the call being
evaluated in eval.cpp when the size is non-constant. I think I could get
an error for symbolic phase calls, but that seems a little less
interesting already. Long-term maybe we want a way to mark functions as
_must_ be evaluated during constant phase?

Also, I think there may be a bug with literal value parse node
locations, I should be able to point at the position of `arg_ids[0]` but
it's missing a line number so I point at `loc` instead.
This commit is contained in:
Jon Ross-Perkins
2024-04-04 23:47:06 +00:00
committed by GitHub
parent f44643f080
commit 6889aff49a
9 changed files with 360 additions and 0 deletions
+32
View File
@@ -302,6 +302,19 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst)
return context.constant_values().Get(elements[index_val.getZExtValue()]);
}
// Enforces that the bit width is 64 for a float.
static auto ValidateFloatBitWidth(Context& context, SemIRLoc loc,
SemIR::InstId inst_id) -> bool {
auto inst = context.insts().GetAs<SemIR::IntLiteral>(inst_id);
if (context.ints().Get(inst.int_id) == 64) {
return true;
}
CARBON_DIAGNOSTIC(CompileTimeFloatBitWidth, Error, "Bit width must be 64.");
context.emitter().Emit(loc, CompileTimeFloatBitWidth);
return false;
}
// Issues a diagnostic for a compile-time division by zero.
static auto DiagnoseDivisionByZero(Context& context, SemIRLoc loc) -> void {
CARBON_DIAGNOSTIC(CompileTimeDivisionByZero, Error, "Division by zero.");
@@ -497,6 +510,25 @@ static auto PerformBuiltinCall(Context& context, SemIRLoc loc, SemIR::Call call,
case SemIR::BuiltinFunctionKind::None:
CARBON_FATAL() << "Not a builtin function.";
case SemIR::BuiltinFunctionKind::IntMakeType32: {
return context.constant_values().Get(SemIR::InstId::BuiltinIntType);
}
case SemIR::BuiltinFunctionKind::FloatMakeType: {
// TODO: Support a symbolic constant width.
if (phase != Phase::Template) {
break;
}
if (!ValidateFloatBitWidth(context, loc, arg_ids[0])) {
return SemIR::ConstantId::Error;
}
return context.constant_values().Get(SemIR::InstId::BuiltinFloatType);
}
case SemIR::BuiltinFunctionKind::BoolMakeType: {
return context.constant_values().Get(SemIR::InstId::BuiltinBoolType);
}
// Unary integer -> integer operations.
case SemIR::BuiltinFunctionKind::IntNegate:
case SemIR::BuiltinFunctionKind::IntComplement: {
+62
View File
@@ -0,0 +1,62 @@
// 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 Bool() -> type = "bool.make_type";
// --- use_types.carbon
library "uses_types" api;
import library "types";
var b: Bool() = false;
// CHECK:STDOUT: --- types.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Bool = %Bool
// CHECK:STDOUT: }
// CHECK:STDOUT: %Bool: <function> = fn_decl @Bool [template] {
// CHECK:STDOUT: %return.var: ref type = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Bool() -> type = "bool.make_type";
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_types.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: bool = bool_literal false [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Bool = %import_ref
// CHECK:STDOUT: .b = %b
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: <function> = import_ref ir2, inst+2, loc_11 [template = imports.%Bool]
// CHECK:STDOUT: %Bool.ref: <function> = name_ref Bool, %import_ref [template = imports.%Bool]
// CHECK:STDOUT: %.loc6_12.1: init type = call %Bool.ref() [template = bool]
// CHECK:STDOUT: %.loc6_13: type = value_of_initializer %.loc6_12.1 [template = bool]
// CHECK:STDOUT: %.loc6_12.2: type = converted %.loc6_12.1, %.loc6_13 [template = bool]
// CHECK:STDOUT: %b.var: ref bool = var b
// CHECK:STDOUT: %b: ref bool = bind_name b, %b.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Bool() -> type = "bool.make_type";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc6: bool = bool_literal false [template = constants.%.1]
// CHECK:STDOUT: assign file.%b.var, %.loc6
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+147
View File
@@ -0,0 +1,147 @@
// 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 Float(size: i32) -> type = "float.make_type";
// --- use_types.carbon
library "uses_types" api;
import library "types";
var f: Float(64) = 0.0;
fn GetFloat(dyn_size: i32) -> type {
return Float(dyn_size);
}
// --- fail_invalid_size.carbon
library "invalid_size" api;
import library "types";
// CHECK:STDERR: fail_invalid_size.carbon:[[@LINE+4]]:20: ERROR: Bit width must be 64.
// CHECK:STDERR: var invalid_float: Float(32);
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
var invalid_float: Float(32);
var dyn_size: i32 = 64;
// CHECK:STDERR: fail_invalid_size.carbon:[[@LINE+3]]:10: ERROR: Cannot evaluate type expression.
// CHECK:STDERR: var dyn: Float(dyn_size);
// CHECK:STDERR: ^~~~~~~~~~~~~~~
var dyn: Float(dyn_size);
// CHECK:STDOUT: --- types.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Float = %Float
// CHECK:STDOUT: }
// CHECK:STDOUT: %Float: <function> = fn_decl @Float [template] {
// CHECK:STDOUT: %size.loc4_10.1: i32 = param size
// CHECK:STDOUT: @Float.%size: i32 = bind_name size, %size.loc4_10.1
// CHECK:STDOUT: %return.var: ref type = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Float(%size: i32) -> type = "float.make_type";
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_types.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: i32 = int_literal 64 [template]
// CHECK:STDOUT: %.2: f64 = real_literal 0e-1 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Float = %import_ref
// CHECK:STDOUT: .f = %f
// CHECK:STDOUT: .GetFloat = %GetFloat
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: <function> = import_ref ir2, inst+4, loc_11 [template = imports.%Float]
// CHECK:STDOUT: %Float.ref: <function> = name_ref Float, %import_ref [template = imports.%Float]
// CHECK:STDOUT: %.loc6_14: i32 = int_literal 64 [template = constants.%.1]
// CHECK:STDOUT: %.loc6_13.1: init type = call %Float.ref(%.loc6_14) [template = f64]
// CHECK:STDOUT: %.loc6_16: type = value_of_initializer %.loc6_13.1 [template = f64]
// CHECK:STDOUT: %.loc6_13.2: type = converted %.loc6_13.1, %.loc6_16 [template = f64]
// CHECK:STDOUT: %f.var: ref f64 = var f
// CHECK:STDOUT: %f: ref f64 = bind_name f, %f.var
// CHECK:STDOUT: %GetFloat: <function> = fn_decl @GetFloat [template] {
// CHECK:STDOUT: %dyn_size.loc8_13.1: i32 = param dyn_size
// CHECK:STDOUT: @GetFloat.%dyn_size: i32 = bind_name dyn_size, %dyn_size.loc8_13.1
// CHECK:STDOUT: %return.var: ref type = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Float(%size: i32) -> type = "float.make_type";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @GetFloat(%dyn_size: i32) -> type {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Float.ref: <function> = name_ref Float, file.%import_ref [template = imports.%Float]
// CHECK:STDOUT: %dyn_size.ref: i32 = name_ref dyn_size, %dyn_size
// CHECK:STDOUT: %.loc9_15.1: init type = call %Float.ref(%dyn_size.ref)
// CHECK:STDOUT: %.loc9_25: type = value_of_initializer %.loc9_15.1
// CHECK:STDOUT: %.loc9_15.2: type = converted %.loc9_15.1, %.loc9_25
// CHECK:STDOUT: return %.loc9_15.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc6: f64 = real_literal 0e-1 [template = constants.%.2]
// CHECK:STDOUT: assign file.%f.var, %.loc6
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_invalid_size.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: i32 = int_literal 32 [template]
// CHECK:STDOUT: %.2: i32 = int_literal 64 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Float = %import_ref
// CHECK:STDOUT: .invalid_float = %invalid_float
// CHECK:STDOUT: .dyn_size = %dyn_size
// CHECK:STDOUT: .dyn = %dyn
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: <function> = import_ref ir2, inst+4, loc_11 [template = imports.%Float]
// CHECK:STDOUT: %Float.ref.loc10: <function> = name_ref Float, %import_ref [template = imports.%Float]
// CHECK:STDOUT: %.loc10_26: i32 = int_literal 32 [template = constants.%.1]
// CHECK:STDOUT: %.loc10_25.1: init type = call %Float.ref.loc10(%.loc10_26) [template = <error>]
// CHECK:STDOUT: %.loc10_28: type = value_of_initializer %.loc10_25.1 [template = <error>]
// CHECK:STDOUT: %.loc10_25.2: type = converted %.loc10_25.1, %.loc10_28 [template = <error>]
// CHECK:STDOUT: %invalid_float.var: ref <error> = var invalid_float
// CHECK:STDOUT: %invalid_float: ref <error> = bind_name invalid_float, %invalid_float.var
// CHECK:STDOUT: %dyn_size.var: ref i32 = var dyn_size
// CHECK:STDOUT: %dyn_size: ref i32 = bind_name dyn_size, %dyn_size.var
// CHECK:STDOUT: %Float.ref.loc16: <function> = name_ref Float, %import_ref [template = imports.%Float]
// CHECK:STDOUT: %dyn_size.ref: ref i32 = name_ref dyn_size, %dyn_size
// CHECK:STDOUT: %.loc16_16: i32 = bind_value %dyn_size.ref
// CHECK:STDOUT: %.loc16_15.1: init type = call %Float.ref.loc16(%.loc16_16)
// CHECK:STDOUT: %.loc16_24: type = value_of_initializer %.loc16_15.1
// CHECK:STDOUT: %.loc16_15.2: type = converted %.loc16_15.1, %.loc16_24
// CHECK:STDOUT: %dyn.var: ref <error> = var dyn
// CHECK:STDOUT: %dyn: ref <error> = bind_name dyn, %dyn.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Float(%size: i32) -> type = "float.make_type";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc12: i32 = int_literal 64 [template = constants.%.2]
// CHECK:STDOUT: assign file.%dyn_size.var, %.loc12
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -0,0 +1,62 @@
// 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() -> type = "int.make_type_32";
// --- use_types.carbon
library "uses_types" api;
import library "types";
var i: Int() = 0;
// CHECK:STDOUT: --- types.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Int = %Int
// CHECK:STDOUT: }
// CHECK:STDOUT: %Int: <function> = fn_decl @Int [template] {
// CHECK:STDOUT: %return.var: ref type = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Int() -> type = "int.make_type_32";
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_types.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> = namespace [template] {
// CHECK:STDOUT: .Int = %import_ref
// CHECK:STDOUT: .i = %i
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: <function> = import_ref ir2, inst+2, loc_11 [template = imports.%Int]
// CHECK:STDOUT: %Int.ref: <function> = name_ref Int, %import_ref [template = imports.%Int]
// CHECK:STDOUT: %.loc6_11.1: init type = call %Int.ref() [template = i32]
// CHECK:STDOUT: %.loc6_12: type = value_of_initializer %.loc6_11.1 [template = i32]
// CHECK:STDOUT: %.loc6_11.2: type = converted %.loc6_11.1, %.loc6_12 [template = i32]
// CHECK:STDOUT: %i.var: ref i32 = var i
// CHECK:STDOUT: %i: ref i32 = bind_name i, %i.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Int() -> type = "int.make_type_32";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc6: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: assign file.%i.var, %.loc6
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -247,6 +247,7 @@ CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop)
CARBON_DIAGNOSTIC_KIND(CompileTimeDivisionByZero)
CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerOverflow)
CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerNegateOverflow)
CARBON_DIAGNOSTIC_KIND(CompileTimeFloatBitWidth)
CARBON_DIAGNOSTIC_KIND(CompileTimeShiftOutOfRange)
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType)
+6
View File
@@ -209,6 +209,12 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id,
case SemIR::BuiltinFunctionKind::None:
CARBON_FATAL() << "No callee in function call.";
case SemIR::BuiltinFunctionKind::BoolMakeType:
case SemIR::BuiltinFunctionKind::FloatMakeType:
case SemIR::BuiltinFunctionKind::IntMakeType32:
context.SetLocal(inst_id, context.GetTypeAsValue());
return;
case SemIR::BuiltinFunctionKind::IntNegate: {
// Lower `-x` as `0 - x`.
auto* operand = context.GetValue(arg_ids[0]);
+28
View File
@@ -0,0 +1,28 @@
// 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 Int() -> type = "int.make_type_32";
fn Float(size: i32) -> type = "float.make_type";
fn Bool() -> type = "bool.make_type";
fn F() {
var i: Int() = 0;
var f: Float(64) = 0.0;
var b: Bool() = false;
}
// CHECK:STDOUT: ; ModuleID = 'types.carbon'
// CHECK:STDOUT: source_filename = "types.carbon"
// CHECK:STDOUT:
// CHECK:STDOUT: define void @F() {
// CHECK:STDOUT: %i = alloca i32, align 4
// CHECK:STDOUT: store i32 0, ptr %i, align 4
// CHECK:STDOUT: %f = alloca double, align 8
// CHECK:STDOUT: store double 0.000000e+00, ptr %f, align 8
// CHECK:STDOUT: %b = alloca i1, align 1
// CHECK:STDOUT: store i1 false, ptr %b, align 1
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
@@ -68,6 +68,10 @@ using Bool = BuiltinType<InstId::BuiltinBoolType>;
// 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<InstId::BuiltinIntType>;
// Constraint that requires the type to be the type type.
using Type = BuiltinType<InstId::BuiltinTypeType>;
} // namespace
// Validates that this builtin has a signature matching the specified signature.
@@ -132,6 +136,19 @@ using IntU = TypeParam<1, AnyInt>;
// Not a builtin function.
constexpr BuiltinInfo None = {"", nullptr};
// Returns the `i32` type. Doesn't take a bit size because we need an integer
// type as a basis for that.
constexpr BuiltinInfo IntMakeType32 = {"int.make_type_32",
ValidateSignature<auto()->Type>};
// Returns float types, such as `f64`. Currently only supports `f64`.
constexpr BuiltinInfo FloatMakeType = {"float.make_type",
ValidateSignature<auto(IntT)->Type>};
// Returns the `bool` type.
constexpr BuiltinInfo BoolMakeType = {"bool.make_type",
ValidateSignature<auto()->Type>};
// "int.negate": integer negation.
constexpr BuiltinInfo IntNegate = {"int.negate",
ValidateSignature<auto(IntT)->IntT>};
@@ -18,6 +18,11 @@
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(None)
// Type factories.
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeType32)
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatMakeType)
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolMakeType)
// Integer arithmetic.
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntNegate)
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntAdd)