Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert array bounds to IntLiteral. #4526

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions core/prelude/operators/as.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

package Core library "prelude/operators/as";

// For Core.Int, used below.
// TODO: Remove this when the below `impl`s are removed.
import library "prelude/types";

interface As(Dest:! type) {
fn Convert[self: Self]() -> Dest;
}
Expand All @@ -12,3 +16,54 @@ interface ImplicitAs(Dest:! type) {
// TODO: extend As(Dest);
fn Convert[self: Self]() -> Dest;
}

// TODO: These impls should live with Core.Int, but currently that's a builtin
// not a class type, so there is no other library these can go in.
impl IntLiteral() as ImplicitAs(i32) {
fn Convert[self: Self]() -> i32 = "int.convert_checked";
}

impl forall [N:! IntLiteral()] IntLiteral() as ImplicitAs(Int(N)) {
fn Convert[self: Self]() -> Int(N) = "int.convert_checked";
}

impl forall [N:! IntLiteral()] IntLiteral() as ImplicitAs(UInt(N)) {
fn Convert[self: Self]() -> UInt(N) = "int.convert_checked";
}

impl i32 as ImplicitAs(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

impl forall [N:! IntLiteral()] Int(N) as ImplicitAs(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

impl forall [N:! IntLiteral()] UInt(N) as ImplicitAs(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

// TODO: Remove these once ImplicitAs extends As.
impl IntLiteral() as As(i32) {
fn Convert[self: Self]() -> i32 = "int.convert_checked";
}

impl forall [N:! IntLiteral()] IntLiteral() as As(Int(N)) {
fn Convert[self: Self]() -> Int(N) = "int.convert_checked";
}

impl forall [N:! IntLiteral()] IntLiteral() as As(UInt(N)) {
fn Convert[self: Self]() -> UInt(N) = "int.convert_checked";
}

impl i32 as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

impl forall [N:! IntLiteral()] Int(N) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}

impl forall [N:! IntLiteral()] UInt(N) as As(IntLiteral()) {
fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked";
}
35 changes: 18 additions & 17 deletions toolchain/check/call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ enum class EntityKind : uint8_t {
static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
const SemIR::EntityWithParamsBase& entity,
EntityKind entity_kind_for_diagnostic,
SemIR::GenericId entity_generic_id,
SemIR::SpecificId enclosing_specific_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
Expand Down Expand Up @@ -70,9 +69,9 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,

// Perform argument deduction.
auto specific_id = SemIR::SpecificId::Invalid;
if (entity_generic_id.is_valid()) {
if (entity.generic_id.is_valid()) {
specific_id = DeduceGenericCallArguments(
context, loc_id, entity_generic_id, enclosing_specific_id,
context, loc_id, entity.generic_id, enclosing_specific_id,
entity.implicit_param_patterns_id, entity.param_patterns_id, self_id,
arg_ids);
if (!specific_id.is_valid()) {
Expand All @@ -90,10 +89,10 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::InstId {
const auto& generic_class = context.classes().Get(class_id);
auto callee_specific_id = ResolveCalleeInCall(
context, loc_id, generic_class, EntityKind::GenericClass,
generic_class.generic_id, enclosing_specific_id,
/*self_id=*/SemIR::InstId::Invalid, arg_ids);
auto callee_specific_id =
ResolveCalleeInCall(context, loc_id, generic_class,
EntityKind::GenericClass, enclosing_specific_id,
/*self_id=*/SemIR::InstId::Invalid, arg_ids);
if (!callee_specific_id) {
return SemIR::InstId::BuiltinError;
}
Expand All @@ -110,10 +109,10 @@ static auto PerformCallToGenericInterface(
SemIR::SpecificId enclosing_specific_id,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
const auto& interface = context.interfaces().Get(interface_id);
auto callee_specific_id = ResolveCalleeInCall(
context, loc_id, interface, EntityKind::GenericInterface,
interface.generic_id, enclosing_specific_id,
/*self_id=*/SemIR::InstId::Invalid, arg_ids);
auto callee_specific_id =
ResolveCalleeInCall(context, loc_id, interface,
EntityKind::GenericInterface, enclosing_specific_id,
/*self_id=*/SemIR::InstId::Invalid, arg_ids);
if (!callee_specific_id) {
return SemIR::InstId::BuiltinError;
}
Expand Down Expand Up @@ -149,13 +148,13 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
}
}
}
auto& function = context.functions().Get(callee_function.function_id);

// If the callee is a generic function, determine the generic argument values
// for the call.
auto callee_specific_id = ResolveCalleeInCall(
context, loc_id, function, EntityKind::Function, function.generic_id,
callee_function.enclosing_specific_id, callee_function.self_id, arg_ids);
context, loc_id, context.functions().Get(callee_function.function_id),
EntityKind::Function, callee_function.enclosing_specific_id,
callee_function.self_id, arg_ids);
if (!callee_specific_id) {
return SemIR::InstId::BuiltinError;
}
Expand All @@ -173,6 +172,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
// If there is a return slot, build storage for the result.
SemIR::InstId return_slot_arg_id = SemIR::InstId::Invalid;
SemIR::ReturnTypeInfo return_info = [&] {
auto& function = context.functions().Get(callee_function.function_id);
DiagnosticAnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note,
Expand Down Expand Up @@ -206,9 +206,10 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
}

// Convert the arguments to match the parameters.
auto converted_args_id =
ConvertCallArgs(context, loc_id, callee_function.self_id, arg_ids,
return_slot_arg_id, function, *callee_specific_id);
auto converted_args_id = ConvertCallArgs(
context, loc_id, callee_function.self_id, arg_ids, return_slot_arg_id,
context.functions().Get(callee_function.function_id),
*callee_specific_id);
auto call_inst_id =
context.GetOrAddInst<SemIR::Call>(loc_id, {.type_id = return_info.type_id,
.callee_id = callee_id,
Expand Down
3 changes: 3 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,9 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc,

// Integer conversions.
case SemIR::BuiltinFunctionKind::IntConvertChecked: {
if (phase == Phase::Symbolic) {
return MakeConstantResult(context, call, phase);
}
return PerformCheckedIntConvert(context, loc, arg_ids[0], call.type_id);
}

Expand Down
18 changes: 12 additions & 6 deletions toolchain/check/handle_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "toolchain/check/convert.h"
#include "toolchain/check/handle.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/sem_ir/builtin_inst_kind.h"

namespace Carbon::Check {

Expand Down Expand Up @@ -33,24 +34,29 @@ auto HandleParseNode(Context& context, Parse::ArrayExprId node_id) -> bool {
auto [element_type_node_id, element_type_inst_id] =
context.node_stack().PopExprWithNodeId();

// The array bound must be a constant.
auto element_type_id =
ExprAsType(context, element_type_node_id, element_type_inst_id).type_id;

// The array bound must be a constant. Diagnose this prior to conversion
// because conversion to `IntLiteral` will produce a generic "non-constant
// call to compile-time-only function" error.
//
// TODO: Should we support runtime-phase bounds in cases such as:
// comptime fn F(n: i32) -> type { return [i32; n]; }
auto bound_inst = context.constant_values().Get(bound_inst_id);
if (!bound_inst.is_constant()) {
if (!context.constant_values().Get(bound_inst_id).is_constant()) {
CARBON_DIAGNOSTIC(InvalidArrayExpr, Error, "array bound is not a constant");
context.emitter().Emit(bound_inst_id, InvalidArrayExpr);
context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);
return true;
}

bound_inst_id = ConvertToValueOfType(
context, context.insts().GetLocId(bound_inst_id), bound_inst_id,
context.GetBuiltinType(SemIR::BuiltinInstKind::IntLiteralType));
context.AddInstAndPush<SemIR::ArrayType>(
node_id, {.type_id = SemIR::TypeId::TypeType,
.bound_id = bound_inst_id,
.element_type_id = ExprAsType(context, element_type_node_id,
element_type_inst_id)
.type_id});
.element_type_id = element_type_id});
return true;
}

Expand Down
3 changes: 3 additions & 0 deletions toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ auto HandleParseNode(Context& context,
auto& function = context.functions().Get(function_id);
if (IsValidBuiltinDeclaration(context, function, builtin_kind)) {
function.builtin_function_kind = builtin_kind;
// Build an empty generic definition if this is a generic builtin.
StartGenericDefinition(context);
FinishGenericDefinition(context, function.generic_id);
} else {
CARBON_DIAGNOSTIC(InvalidBuiltinSignature, Error,
"invalid signature for builtin function \"{0}\"",
Expand Down
Loading