diff --git a/common/error.h b/common/error.h index ea92ba8e8e20..496af17c4fb7 100644 --- a/common/error.h +++ b/common/error.h @@ -141,14 +141,14 @@ class ErrorBuilder { // Accumulates string message to a temporary `ErrorBuilder`. After streaming, // the builder must be converted to an `Error` or `ErrorOr`. template - auto operator<<(const T& message) && -> ErrorBuilder&& { + auto operator<<(T&& message) && -> ErrorBuilder&& { *out_ << message; return std::move(*this); } // Accumulates string message for an lvalue error builder. template - auto operator<<(const T& message) & -> ErrorBuilder& { + auto operator<<(T&& message) & -> ErrorBuilder& { *out_ << message; return *this; } diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 39036f6d3a4f..a4b900370d4c 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -4,6 +4,8 @@ #include "toolchain/check/eval.h" +#include "toolchain/diagnostics/diagnostic_emitter.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -283,6 +285,101 @@ static auto PerformAggregateIndex(Context& context, SemIR::Inst inst) return context.constant_values().Get(elements[index_val.getZExtValue()]); } +static auto PerformBuiltinCall(Context& context, SemIRLocation loc, + SemIR::Call call, + SemIR::BuiltinFunctionKind builtin_kind, + llvm::ArrayRef arg_ids, + Phase phase) -> SemIR::ConstantId { + switch (builtin_kind) { + case SemIR::BuiltinFunctionKind::None: + CARBON_FATAL() << "Not a builtin function."; + + case SemIR::BuiltinFunctionKind::IntAdd: { + if (phase != Phase::Template) { + break; + } + if (arg_ids.size() != 2) { + break; + } + auto lhs = context.insts().TryGetAs(arg_ids[0]); + auto rhs = context.insts().TryGetAs(arg_ids[1]); + // TODO: Move type checking to the point where we make the call. + if (!lhs || !rhs || lhs->type_id != rhs->type_id || + call.type_id != lhs->type_id) { + break; + } + // TODO: Integer values should be stored in the correct bit width for + // their types. For now we assume i32. + auto lhs_val = context.ints().Get(lhs->int_id).sextOrTrunc(32); + auto rhs_val = context.ints().Get(rhs->int_id).sextOrTrunc(32); + bool overflow = false; + auto result = context.ints().Add(lhs_val.sadd_ov(rhs_val, overflow)); + if (overflow) { + CARBON_DIAGNOSTIC(CompileTimeIntegerOverflow, Error, + "Integer overflow in calculation {0} + {1}.", + llvm::APSInt, llvm::APSInt); + context.emitter().Emit(loc, CompileTimeIntegerOverflow, + llvm::APSInt(lhs_val, false), + llvm::APSInt(rhs_val, false)); + } + return MakeConstantResult(context, + SemIR::IntLiteral{lhs->type_id, result}, phase); + } + } + + return SemIR::ConstantId::NotConstant; +} + +// Extracts the callee function from a callee constant. Returns +// FunctionId::Invalid if the callee is not known. +static auto GetCalleeFunctionId(Context& context, SemIR::InstId callee_id) + -> SemIR::FunctionId { + if (auto bound_method = + context.insts().TryGetAs(callee_id)) { + callee_id = bound_method->function_id; + } + if (auto callee = context.insts().TryGetAs(callee_id)) { + return {callee->function_id}; + } + return {SemIR::FunctionId::Invalid}; +} + +static auto PerformCall(Context& context, SemIRLocation loc, SemIR::Call call) + -> SemIR::ConstantId { + Phase phase = Phase::Template; + + // A call with an invalid argument list is used to represent an erroneous + // call. + // + // TODO: Use a better representation for this. + if (call.args_id == SemIR::InstBlockId::Invalid) { + return SemIR::ConstantId::Error; + } + + // If the callee isn't constant, this is not a constant call. + if (!ReplaceFieldWithConstantValue(context, &call, &SemIR::Call::callee_id, + &phase)) { + return SemIR::ConstantId::NotConstant; + } + + auto function_id = GetCalleeFunctionId(context, call.callee_id); + + // Handle calls to builtins. + auto& function = context.functions().Get(function_id); + if (function.builtin_kind != SemIR::BuiltinFunctionKind::None) { + if (!ReplaceFieldWithConstantValue(context, &call, &SemIR::Call::args_id, + &phase)) { + return SemIR::ConstantId::NotConstant; + } + if (phase == Phase::UnknownDueToError) { + return SemIR::ConstantId::Error; + } + return PerformBuiltinCall(context, loc, call, function.builtin_kind, + context.inst_blocks().Get(call.args_id), phase); + } + return SemIR::ConstantId::NotConstant; +} + auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { // TODO: Ensure we have test coverage for each of these cases that can result @@ -425,9 +522,11 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) case SemIR::TupleIndex::Kind: return PerformAggregateIndex(context, inst); + case SemIR::Call::Kind: + return PerformCall(context, inst_id, inst.As()); + // TODO: These need special handling. case SemIR::BindValue::Kind: - case SemIR::Call::Kind: case SemIR::Deref::Kind: case SemIR::ImportRefUsed::Kind: case SemIR::Temporary::Kind: diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 27489c5b0afe..843a5b9adb4c 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -284,4 +284,53 @@ auto HandleFunctionDefinition(Context& context, return true; } +auto HandleBuiltinFunctionDefinitionStart( + Context& context, Parse::BuiltinFunctionDefinitionStartId node_id) -> bool { + // Process the declaration portion of the function. + auto [function_id, _] = + BuildFunctionDecl(context, node_id, /*is_definition=*/true); + context.node_stack().Push(node_id, function_id); + return true; +} + +auto HandleBuiltinName(Context& context, Parse::BuiltinNameId node_id) -> bool { + context.node_stack().Push(node_id); + return true; +} + +// Looks up a builtin function kind given its name as a string. +// TODO: Move this out to another file. +static auto LookupBuiltinFunctionKind(Context& context, + Parse::BuiltinNameId name_id) + -> SemIR::BuiltinFunctionKind { + auto builtin_name = context.string_literal_values().Get( + context.tokens().GetStringLiteralValue( + context.parse_tree().node_token(name_id))); + auto kind = llvm::StringSwitch(builtin_name) + .Case("int.add", SemIR::BuiltinFunctionKind::IntAdd) + .Default(SemIR::BuiltinFunctionKind::None); + if (kind == SemIR::BuiltinFunctionKind::None) { + CARBON_DIAGNOSTIC(UnknownBuiltinFunctionName, Error, + "Unknown builtin function name \"{0}\".", std::string); + context.emitter().Emit(name_id, UnknownBuiltinFunctionName, + builtin_name.str()); + } + return kind; +} + +auto HandleBuiltinFunctionDefinition( + Context& context, Parse::BuiltinFunctionDefinitionId /*node_id*/) -> bool { + auto name_id = + context.node_stack().PopForSoloNodeId(); + auto function_id = + context.node_stack() + .Pop(); + + auto& function = context.functions().Get(function_id); + function.builtin_kind = LookupBuiltinFunctionKind(context, name_id); + + context.decl_name_stack().PopScope(); + return true; +} + } // namespace Carbon::Check diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index e22ba5b887a9..e77c4757f179 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -706,7 +706,8 @@ class ImportRefResolver { .param_refs_id = GetLocalParamRefsId(function.param_refs_id, param_const_ids), .return_type_id = new_return_type_id, - .return_slot_id = new_return_slot}); + .return_slot_id = new_return_slot, + .builtin_kind = function.builtin_kind}); // Write the function ID into the FunctionDecl. context_.ReplaceInstBeforeConstantUse( function_decl_id, {Parse::NodeId::Invalid, function_decl}); diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 86ce7fab86cd..421542b0923f 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -424,6 +424,7 @@ class NodeStack { case Parse::NodeKind::WhileConditionStart: return Id::KindFor(); case Parse::NodeKind::FunctionDefinitionStart: + case Parse::NodeKind::BuiltinFunctionDefinitionStart: return Id::KindFor(); case Parse::NodeKind::ClassDefinitionStart: return Id::KindFor(); @@ -434,6 +435,7 @@ class NodeStack { case Parse::NodeKind::SelfValueName: return Id::KindFor(); case Parse::NodeKind::ArrayExprSemi: + case Parse::NodeKind::BuiltinName: case Parse::NodeKind::ClassIntroducer: case Parse::NodeKind::CodeBlockStart: case Parse::NodeKind::ExprOpenParen: diff --git a/toolchain/check/testdata/builtins/int_add.carbon b/toolchain/check/testdata/builtins/int_add.carbon new file mode 100644 index 000000000000..dd7fa840aed1 --- /dev/null +++ b/toolchain/check/testdata/builtins/int_add.carbon @@ -0,0 +1,301 @@ +// 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 + +// --- int_add.carbon + +fn Add(a: i32, b: i32) -> i32 = "int.add"; + +var arr: [i32; Add(1, 2)]; + +fn RuntimeCall(a: i32, b: i32) -> i32 { + return Add(a, b); +} + +// --- fail_bad_decl.carbon + +package FailBadDecl api; + +fn TooFew(a: i32) -> i32 = "int.add"; +fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.add"; +fn BadReturnType(a: i32, b: i32) -> bool = "int.add"; +fn JustRight(a: i32, b: i32) -> i32 = "int.add"; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+3]]:20: ERROR: Array bound is not a constant. +// CHECK:STDERR: var too_few: [i32; TooFew(1)]; +// CHECK:STDERR: ^~~~~~~ +var too_few: [i32; TooFew(1)]; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+3]]:21: ERROR: Array bound is not a constant. +// CHECK:STDERR: var too_many: [i32; TooMany(1, 2, 3)]; +// CHECK:STDERR: ^~~~~~~~ +var too_many: [i32; TooMany(1, 2, 3)]; +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+3]]:28: ERROR: Array bound is not a constant. +// CHECK:STDERR: var bad_return_type: [i32; BadReturnType(1, 2)]; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +var bad_return_type: [i32; BadReturnType(1, 2)]; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+6]]:21: ERROR: 3 argument(s) passed to function expecting 2 argument(s). +// CHECK:STDERR: var bad_call: [i32; JustRight(1, 2, 3)]; +// CHECK:STDERR: ^~~~~~~~~~ +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE-18]]:1: Calling function declared here. +// CHECK:STDERR: fn JustRight(a: i32, b: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var bad_call: [i32; JustRight(1, 2, 3)]; + +// TODO: We should diagnose these in check rather than failing in lower. +fn RuntimeCallTooFew(a: i32) -> i32 { + return TooFew(a); +} + +fn RuntimeCallTooMany(a: i32, b: i32, c: i32) -> i32 { + return TooMany(a, b, c); +} + +fn RuntimeCallBadReturnType(a: i32, b: i32) -> bool { + return BadReturnType(a, b); +} + +// --- fail_overflow.carbon + +package FailOverflow api; + +fn Add(a: i32, b: i32) -> i32 = "int.add"; + +let a: i32 = Add(0x7FFFFFFF, 0); +// CHECK:STDERR: fail_overflow.carbon:[[@LINE+3]]:14: ERROR: Integer overflow in calculation 2147483647 + 1. +// CHECK:STDERR: let b: i32 = Add(0x7FFFFFFF, 1); +// CHECK:STDERR: ^~~~ +let b: i32 = Add(0x7FFFFFFF, 1); + +// CHECK:STDOUT: --- int_add.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 3] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: .RuntimeCall = %RuntimeCall +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add: = fn_decl @Add [template] { +// CHECK:STDOUT: %a.loc2_8.1: i32 = param a +// CHECK:STDOUT: @Add.%a: i32 = bind_name a, %a.loc2_8.1 +// CHECK:STDOUT: %b.loc2_16.1: i32 = param b +// CHECK:STDOUT: @Add.%b: i32 = bind_name b, %b.loc2_16.1 +// CHECK:STDOUT: %return.var.loc2: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add.ref: = name_ref Add, %Add [template = %Add] +// CHECK:STDOUT: %.loc4_20: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_23: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_19: init i32 = call %Add.ref(%.loc4_20, %.loc4_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_25: type = array_type %.loc4_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr +// CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: %RuntimeCall: = fn_decl @RuntimeCall [template] { +// CHECK:STDOUT: %a.loc6_16.1: i32 = param a +// CHECK:STDOUT: @RuntimeCall.%a: i32 = bind_name a, %a.loc6_16.1 +// CHECK:STDOUT: %b.loc6_24.1: i32 = param b +// CHECK:STDOUT: @RuntimeCall.%b: i32 = bind_name b, %b.loc6_24.1 +// CHECK:STDOUT: %return.var.loc6: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCall(%a: i32, %b: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Add.ref: = name_ref Add, file.%Add [template = file.%Add] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc7_13.1: init i32 = call %Add.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc7_19: i32 = value_of_initializer %.loc7_13.1 +// CHECK:STDOUT: %.loc7_13.2: i32 = converted %.loc7_13.1, %.loc7_19 +// CHECK:STDOUT: return %.loc7_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_bad_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 3 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .TooFew = %TooFew +// CHECK:STDOUT: .TooMany = %TooMany +// CHECK:STDOUT: .BadReturnType = %BadReturnType +// CHECK:STDOUT: .JustRight = %JustRight +// CHECK:STDOUT: .too_few = %too_few +// CHECK:STDOUT: .too_many = %too_many +// CHECK:STDOUT: .bad_return_type = %bad_return_type +// CHECK:STDOUT: .bad_call = %bad_call +// CHECK:STDOUT: .RuntimeCallTooFew = %RuntimeCallTooFew +// CHECK:STDOUT: .RuntimeCallTooMany = %RuntimeCallTooMany +// CHECK:STDOUT: .RuntimeCallBadReturnType = %RuntimeCallBadReturnType +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooFew: = fn_decl @TooFew [template] { +// CHECK:STDOUT: %a.loc4_11.1: i32 = param a +// CHECK:STDOUT: @TooFew.%a: i32 = bind_name a, %a.loc4_11.1 +// CHECK:STDOUT: %return.var.loc4: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooMany: = fn_decl @TooMany [template] { +// CHECK:STDOUT: %a.loc5_12.1: i32 = param a +// CHECK:STDOUT: @TooMany.%a: i32 = bind_name a, %a.loc5_12.1 +// CHECK:STDOUT: %b.loc5_20.1: i32 = param b +// CHECK:STDOUT: @TooMany.%b: i32 = bind_name b, %b.loc5_20.1 +// CHECK:STDOUT: %c.loc5_28.1: i32 = param c +// CHECK:STDOUT: @TooMany.%c: i32 = bind_name c, %c.loc5_28.1 +// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %BadReturnType: = fn_decl @BadReturnType [template] { +// CHECK:STDOUT: %a.loc6_18.1: i32 = param a +// CHECK:STDOUT: @BadReturnType.%a: i32 = bind_name a, %a.loc6_18.1 +// CHECK:STDOUT: %b.loc6_26.1: i32 = param b +// CHECK:STDOUT: @BadReturnType.%b: i32 = bind_name b, %b.loc6_26.1 +// CHECK:STDOUT: %return.var.loc6: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %JustRight: = fn_decl @JustRight [template] { +// CHECK:STDOUT: %a.loc7_14.1: i32 = param a +// CHECK:STDOUT: @JustRight.%a: i32 = bind_name a, %a.loc7_14.1 +// CHECK:STDOUT: %b.loc7_22.1: i32 = param b +// CHECK:STDOUT: @JustRight.%b: i32 = bind_name b, %b.loc7_22.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %TooFew.ref: = name_ref TooFew, %TooFew [template = %TooFew] +// CHECK:STDOUT: %.loc12_27: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc12_26: init i32 = call %TooFew.ref(%.loc12_27) +// CHECK:STDOUT: %too_few.var: ref = var too_few +// CHECK:STDOUT: %too_few: ref = bind_name too_few, %too_few.var +// CHECK:STDOUT: %TooMany.ref: = name_ref TooMany, %TooMany [template = %TooMany] +// CHECK:STDOUT: %.loc16_29: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc16_32: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc16_35: i32 = int_literal 3 [template = constants.%.3] +// CHECK:STDOUT: %.loc16_28: init i32 = call %TooMany.ref(%.loc16_29, %.loc16_32, %.loc16_35) +// CHECK:STDOUT: %too_many.var: ref = var too_many +// CHECK:STDOUT: %too_many: ref = bind_name too_many, %too_many.var +// CHECK:STDOUT: %BadReturnType.ref: = name_ref BadReturnType, %BadReturnType [template = %BadReturnType] +// CHECK:STDOUT: %.loc20_42: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc20_45: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc20_41: init bool = call %BadReturnType.ref(%.loc20_42, %.loc20_45) +// CHECK:STDOUT: %bad_return_type.var: ref = var bad_return_type +// CHECK:STDOUT: %bad_return_type: ref = bind_name bad_return_type, %bad_return_type.var +// CHECK:STDOUT: %JustRight.ref: = name_ref JustRight, %JustRight [template = %JustRight] +// CHECK:STDOUT: %.loc28_31: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc28_34: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc28_37: i32 = int_literal 3 [template = constants.%.3] +// CHECK:STDOUT: %.loc28_30: init i32 = call %JustRight.ref() [template = ] +// CHECK:STDOUT: %.loc28_39: type = array_type %.loc28_30, i32 [template = ] +// CHECK:STDOUT: %bad_call.var: ref = var bad_call +// CHECK:STDOUT: %bad_call: ref = bind_name bad_call, %bad_call.var +// CHECK:STDOUT: %RuntimeCallTooFew: = fn_decl @RuntimeCallTooFew [template] { +// CHECK:STDOUT: %a.loc31_22.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallTooFew.%a: i32 = bind_name a, %a.loc31_22.1 +// CHECK:STDOUT: %return.var.loc31: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %RuntimeCallTooMany: = fn_decl @RuntimeCallTooMany [template] { +// CHECK:STDOUT: %a.loc35_23.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallTooMany.%a: i32 = bind_name a, %a.loc35_23.1 +// CHECK:STDOUT: %b.loc35_31.1: i32 = param b +// CHECK:STDOUT: @RuntimeCallTooMany.%b: i32 = bind_name b, %b.loc35_31.1 +// CHECK:STDOUT: %c.loc35_39.1: i32 = param c +// CHECK:STDOUT: @RuntimeCallTooMany.%c: i32 = bind_name c, %c.loc35_39.1 +// CHECK:STDOUT: %return.var.loc35: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %RuntimeCallBadReturnType: = fn_decl @RuntimeCallBadReturnType [template] { +// CHECK:STDOUT: %a.loc39_29.1: i32 = param a +// CHECK:STDOUT: @RuntimeCallBadReturnType.%a: i32 = bind_name a, %a.loc39_29.1 +// CHECK:STDOUT: %b.loc39_37.1: i32 = param b +// CHECK:STDOUT: @RuntimeCallBadReturnType.%b: i32 = bind_name b, %b.loc39_37.1 +// CHECK:STDOUT: %return.var.loc39: ref bool = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TooFew(%a: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TooMany(%a: i32, %b: i32, %c: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @BadReturnType(%a: i32, %b: i32) -> bool = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @JustRight(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallTooFew(%a: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %TooFew.ref: = name_ref TooFew, file.%TooFew [template = file.%TooFew] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %.loc32_16.1: init i32 = call %TooFew.ref(%a.ref) +// CHECK:STDOUT: %.loc32_19: i32 = value_of_initializer %.loc32_16.1 +// CHECK:STDOUT: %.loc32_16.2: i32 = converted %.loc32_16.1, %.loc32_19 +// CHECK:STDOUT: return %.loc32_16.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallTooMany(%a: i32, %b: i32, %c: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %TooMany.ref: = name_ref TooMany, file.%TooMany [template = file.%TooMany] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %c.ref: i32 = name_ref c, %c +// CHECK:STDOUT: %.loc36_17.1: init i32 = call %TooMany.ref(%a.ref, %b.ref, %c.ref) +// CHECK:STDOUT: %.loc36_26: i32 = value_of_initializer %.loc36_17.1 +// CHECK:STDOUT: %.loc36_17.2: i32 = converted %.loc36_17.1, %.loc36_26 +// CHECK:STDOUT: return %.loc36_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @RuntimeCallBadReturnType(%a: i32, %b: i32) -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %BadReturnType.ref: = name_ref BadReturnType, file.%BadReturnType [template = file.%BadReturnType] +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc40_23.1: init bool = call %BadReturnType.ref(%a.ref, %b.ref) +// CHECK:STDOUT: %.loc40_29: bool = value_of_initializer %.loc40_23.1 +// CHECK:STDOUT: %.loc40_23.2: bool = converted %.loc40_23.1, %.loc40_29 +// CHECK:STDOUT: return %.loc40_23.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_overflow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 2147483647 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 2147483648 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add: = fn_decl @Add [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Add.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Add.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add.ref.loc6: = name_ref Add, %Add [template = %Add] +// CHECK:STDOUT: %.loc6_18: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_30: i32 = int_literal 0 [template = constants.%.2] +// CHECK:STDOUT: %.loc6_17.1: init i32 = call %Add.ref.loc6(%.loc6_18, %.loc6_30) [template = constants.%.1] +// CHECK:STDOUT: %.loc6_32: i32 = value_of_initializer %.loc6_17.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc6_17.2: i32 = converted %.loc6_17.1, %.loc6_32 [template = constants.%.1] +// CHECK:STDOUT: %a.loc6: i32 = bind_name a, %.loc6_17.2 +// CHECK:STDOUT: %Add.ref.loc10: = name_ref Add, %Add [template = %Add] +// CHECK:STDOUT: %.loc10_18: i32 = int_literal 2147483647 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_30: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_17.1: init i32 = call %Add.ref.loc10(%.loc10_18, %.loc10_30) [template = constants.%.4] +// CHECK:STDOUT: %.loc10_32: i32 = value_of_initializer %.loc10_17.1 [template = constants.%.4] +// CHECK:STDOUT: %.loc10_17.2: i32 = converted %.loc10_17.1, %.loc10_32 [template = constants.%.4] +// CHECK:STDOUT: %b.loc10: i32 = bind_name b, %.loc10_17.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_addr_self.carbon b/toolchain/check/testdata/class/fail_addr_self.carbon index a636a6e99716..a2b8f17662d8 100644 --- a/toolchain/check/testdata/class/fail_addr_self.carbon +++ b/toolchain/check/testdata/class/fail_addr_self.carbon @@ -95,11 +95,11 @@ fn F(c: Class, p: Class*) { // CHECK:STDOUT: %c.ref.loc19: Class = name_ref c, %c // CHECK:STDOUT: %F.ref.loc19: = name_ref F, @Class.%F [template = @Class.%F] // CHECK:STDOUT: %.loc19_4: = bound_method %c.ref.loc19, %F.ref.loc19 -// CHECK:STDOUT: %.loc19_6: init () = call %.loc19_4() +// CHECK:STDOUT: %.loc19_6: init () = call %.loc19_4() [template = ] // CHECK:STDOUT: %c.ref.loc27: Class = name_ref c, %c // CHECK:STDOUT: %G.ref.loc27: = name_ref G, @Class.%G [template = @Class.%G] // CHECK:STDOUT: %.loc27_4: = bound_method %c.ref.loc27, %G.ref.loc27 -// CHECK:STDOUT: %.loc27_6: init () = call %.loc27_4() +// CHECK:STDOUT: %.loc27_6: init () = call %.loc27_4() [template = ] // CHECK:STDOUT: %p.ref.loc30: Class* = name_ref p, %p // CHECK:STDOUT: %.loc30_4.1: ref Class = deref %p.ref.loc30 // CHECK:STDOUT: %F.ref.loc30: = name_ref F, @Class.%F [template = @Class.%F] @@ -111,7 +111,7 @@ fn F(c: Class, p: Class*) { // CHECK:STDOUT: %G.ref.loc38: = name_ref G, @Class.%G [template = @Class.%G] // CHECK:STDOUT: %.loc38_7: = bound_method %.loc38_4.1, %G.ref.loc38 // CHECK:STDOUT: %.loc38_4.2: Class* = addr_of %.loc38_4.1 -// CHECK:STDOUT: %.loc38_9: init () = call %.loc38_7() +// CHECK:STDOUT: %.loc38_9: init () = call %.loc38_7() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_incomplete.carbon b/toolchain/check/testdata/class/fail_incomplete.carbon index afd3dd23f9b6..3c2ab1f9c82e 100644 --- a/toolchain/check/testdata/class/fail_incomplete.carbon +++ b/toolchain/check/testdata/class/fail_incomplete.carbon @@ -259,10 +259,10 @@ fn CallReturnIncomplete() { // CHECK:STDOUT: %TakeIncomplete.ref.loc105: = name_ref TakeIncomplete, file.%TakeIncomplete [template = file.%TakeIncomplete] // CHECK:STDOUT: %p.ref: Class* = name_ref p, %p // CHECK:STDOUT: %.loc105_18: ref Class = deref %p.ref -// CHECK:STDOUT: %.loc105_17: init () = call %TakeIncomplete.ref.loc105() +// CHECK:STDOUT: %.loc105_17: init () = call %TakeIncomplete.ref.loc105() [template = ] // CHECK:STDOUT: %TakeIncomplete.ref.loc116: = name_ref TakeIncomplete, file.%TakeIncomplete [template = file.%TakeIncomplete] // CHECK:STDOUT: %.loc116_19: {} = struct_literal () -// CHECK:STDOUT: %.loc116_17: init () = call %TakeIncomplete.ref.loc116() +// CHECK:STDOUT: %.loc116_17: init () = call %TakeIncomplete.ref.loc116() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_memaccess_category.carbon b/toolchain/check/testdata/class/fail_memaccess_category.carbon index 0fdb6adb67ca..4b996cc103ca 100644 --- a/toolchain/check/testdata/class/fail_memaccess_category.carbon +++ b/toolchain/check/testdata/class/fail_memaccess_category.carbon @@ -98,14 +98,14 @@ fn F(s: {.a: A}, b: B) { // CHECK:STDOUT: %.loc23_4: A = struct_access %s.ref, element0 // CHECK:STDOUT: %F.ref.loc23: = name_ref F, @A.%F [template = @A.%F] // CHECK:STDOUT: %.loc23_6: = bound_method %.loc23_4, %F.ref.loc23 -// CHECK:STDOUT: %.loc23_8: init () = call %.loc23_6() +// CHECK:STDOUT: %.loc23_8: init () = call %.loc23_6() [template = ] // CHECK:STDOUT: %b.ref: B = name_ref b, %b // CHECK:STDOUT: %a.ref: = name_ref a, @B.%.loc12 [template = @B.%.loc12] // CHECK:STDOUT: %.loc33_4.1: ref A = class_element_access %b.ref, element0 // CHECK:STDOUT: %.loc33_4.2: A = bind_value %.loc33_4.1 // CHECK:STDOUT: %F.ref.loc33: = name_ref F, @A.%F [template = @A.%F] // CHECK:STDOUT: %.loc33_6: = bound_method %.loc33_4.2, %F.ref.loc33 -// CHECK:STDOUT: %.loc33_8: init () = call %.loc33_6() +// CHECK:STDOUT: %.loc33_8: init () = call %.loc33_6() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_method.carbon b/toolchain/check/testdata/class/fail_method.carbon index d31f9647eb54..9b45f788458a 100644 --- a/toolchain/check/testdata/class/fail_method.carbon +++ b/toolchain/check/testdata/class/fail_method.carbon @@ -98,13 +98,13 @@ fn F(c: Class) { // CHECK:STDOUT: %.loc18: init () = call %NoSelf.ref.loc18() // CHECK:STDOUT: %Class.ref.loc25: type = name_ref Class, file.%Class.decl [template = constants.%Class] // CHECK:STDOUT: %WithSelf.ref.loc25: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] -// CHECK:STDOUT: %.loc25: init () = call %WithSelf.ref.loc25() +// CHECK:STDOUT: %.loc25: init () = call %WithSelf.ref.loc25() [template = ] // CHECK:STDOUT: %Class.ref.loc32: type = name_ref Class, file.%Class.decl [template = constants.%Class] // CHECK:STDOUT: %WithSelf.ref.loc32: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] // CHECK:STDOUT: %c.ref.loc32: Class = name_ref c, %c -// CHECK:STDOUT: %.loc32: init () = call %WithSelf.ref.loc32() +// CHECK:STDOUT: %.loc32: init () = call %WithSelf.ref.loc32() [template = ] // CHECK:STDOUT: %A.ref: = name_ref A, file.%A [template = @Class.%WithSelf] -// CHECK:STDOUT: %.loc40: init () = call %A.ref() +// CHECK:STDOUT: %.loc40: init () = call %A.ref() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_self.carbon b/toolchain/check/testdata/class/fail_self.carbon index 3fda2f1a1fea..9691d42a9bd1 100644 --- a/toolchain/check/testdata/class/fail_self.carbon +++ b/toolchain/check/testdata/class/fail_self.carbon @@ -137,7 +137,7 @@ fn CallWrongSelf(ws: WrongSelf) { // CHECK:STDOUT: %ws.ref: WrongSelf = name_ref ws, %ws // CHECK:STDOUT: %F.ref: = name_ref F, @WrongSelf.%F [template = @WrongSelf.%F] // CHECK:STDOUT: %.loc50_5: = bound_method %ws.ref, %F.ref -// CHECK:STDOUT: %.loc50_7: init () = call %.loc50_5() +// CHECK:STDOUT: %.loc50_7: init () = call %.loc50_5() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/call.carbon b/toolchain/check/testdata/function/builtin/call.carbon new file mode 100644 index 000000000000..f417505a5bfb --- /dev/null +++ b/toolchain/check/testdata/function/builtin/call.carbon @@ -0,0 +1,43 @@ +// 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 Add(a: i32, b: i32) -> i32 = "int.add"; + +var arr: [i32; Add(1, 2)]; + +// CHECK:STDOUT: --- call.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 3] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add: = fn_decl @Add [template] { +// CHECK:STDOUT: %a.loc7_8.1: i32 = param a +// CHECK:STDOUT: @Add.%a: i32 = bind_name a, %a.loc7_8.1 +// CHECK:STDOUT: %b.loc7_16.1: i32 = param b +// CHECK:STDOUT: @Add.%b: i32 = bind_name b, %b.loc7_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add.ref: = name_ref Add, %Add [template = %Add] +// CHECK:STDOUT: %.loc9_20: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc9_23: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc9_19: init i32 = call %Add.ref(%.loc9_20, %.loc9_23) [template = constants.%.3] +// CHECK:STDOUT: %.loc9_25: type = array_type %.loc9_19, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr +// CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/call_from_operator.carbon b/toolchain/check/testdata/function/builtin/call_from_operator.carbon new file mode 100644 index 000000000000..ccfe1da95e9b --- /dev/null +++ b/toolchain/check/testdata/function/builtin/call_from_operator.carbon @@ -0,0 +1,172 @@ +// 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 + +// --- core.carbon + +package Core api; + +interface Add { + fn Op[self: Self](other: Self) -> Self; +} + +// --- user.carbon + +import Core; + +// TODO: This should be in `Core`, but currently impl lookup only looks in the +// current file. +impl i32 as Core.Add { + fn Op[self: i32](other: i32) -> i32 = "int.add"; +} + +var arr: [i32; 1 + 2] = (3, 4, 3 + 4); + +// CHECK:STDOUT: --- core.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @Add [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Add, [template] +// CHECK:STDOUT: %.3: in Add> = assoc_entity element0, @Add.%Op [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add.decl: type = interface_decl @Add [template = constants.%.1] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Add { +// CHECK:STDOUT: %Self: Add = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %Op: = fn_decl @Op [template] { +// CHECK:STDOUT: %Self.ref.loc5_15: Add = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc5_15.1: type = facet_type_access %Self.ref.loc5_15 [symbolic = %Self] +// CHECK:STDOUT: %.loc5_15.2: type = converted %Self.ref.loc5_15, %.loc5_15.1 [symbolic = %Self] +// CHECK:STDOUT: %self.loc5_9.1: Self = param self +// CHECK:STDOUT: %self.loc5_9.2: Self = bind_name self, %self.loc5_9.1 +// CHECK:STDOUT: %Self.ref.loc5_28: Add = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc5_28.1: type = facet_type_access %Self.ref.loc5_28 [symbolic = %Self] +// CHECK:STDOUT: %.loc5_28.2: type = converted %Self.ref.loc5_28, %.loc5_28.1 [symbolic = %Self] +// CHECK:STDOUT: %other.loc5_21.1: Self = param other +// CHECK:STDOUT: %other.loc5_21.2: Self = bind_name other, %other.loc5_21.1 +// CHECK:STDOUT: %Self.ref.loc5_37: Add = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc5_37.1: type = facet_type_access %Self.ref.loc5_37 [symbolic = %Self] +// CHECK:STDOUT: %.loc5_37.2: type = converted %Self.ref.loc5_37, %.loc5_37.1 [symbolic = %Self] +// CHECK:STDOUT: %return.var: ref Self = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc5_41: in Add> = assoc_entity element0, %Op [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Op = %.loc5_41 +// CHECK:STDOUT: witness = (%Op) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Op[@Add.%self.loc5_9.2: Self](@Add.%other.loc5_21.2: Self) -> Self; +// CHECK:STDOUT: +// CHECK:STDOUT: --- user.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @Add [template] +// CHECK:STDOUT: %.2: = interface_witness (@impl.%Op) [template] +// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.5: type = assoc_entity_type @Add, [template] +// CHECK:STDOUT: %.6: in Add> = assoc_entity element0, file.%import_ref.6 [template] +// CHECK:STDOUT: %.7: = bound_method %.3, @impl.%Op [template] +// CHECK:STDOUT: %.8: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.9: type = array_type %.8, i32 [template] +// CHECK:STDOUT: %.10: type = ptr_type [i32; 3] [template] +// CHECK:STDOUT: %.11: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.12: i32 = int_literal 4 [template] +// CHECK:STDOUT: %.13: = bound_method %.11, @impl.%Op [template] +// CHECK:STDOUT: %.14: i32 = int_literal 7 [template] +// CHECK:STDOUT: %.15: type = tuple_type (i32, i32, i32) [template] +// CHECK:STDOUT: %.16: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.17: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.18: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.19: [i32; 3] = tuple_value (%.11, %.12, %.14) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, used [template = constants.%.1] +// CHECK:STDOUT: %import_ref.2: in Add> = import_ref ir1, inst+20, used [template = constants.%.6] +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+3, unused +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+18, used [template = imports.%Op] +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %Core.ref: = name_ref Core, %Core [template = %Core] +// CHECK:STDOUT: %Add.decl: invalid = interface_decl @Add [template = constants.%.1] {} +// CHECK:STDOUT: %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_16: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc10_20: i32 = int_literal 2 [template = constants.%.4] +// CHECK:STDOUT: %import_ref.5: type = import_ref ir1, inst+1, used [template = constants.%.1] +// CHECK:STDOUT: %import_ref.6 = import_ref ir1, inst+18, unused +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%Op] +// CHECK:STDOUT: %.loc10_18.1: = bound_method %.loc10_16, %.1 [template = constants.%.7] +// CHECK:STDOUT: %.loc10_18.2: init i32 = call %.loc10_18.1(%.loc10_16, %.loc10_20) [template = constants.%.8] +// CHECK:STDOUT: %.loc10_21: type = array_type %.loc10_18.2, i32 [template = constants.%.9] +// CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr +// CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: %import_ref.7: type = import_ref ir1, inst+1, used [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Add { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Op = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: witness = (file.%import_ref.4) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: i32 as Add { +// CHECK:STDOUT: %Op: = fn_decl @Op.1 [template] { +// CHECK:STDOUT: %self.loc7_9.1: i32 = param self +// CHECK:STDOUT: %self.loc7_9.2: i32 = bind_name self, %self.loc7_9.1 +// CHECK:STDOUT: %other.loc7_20.1: i32 = param other +// CHECK:STDOUT: %other.loc7_20.2: i32 = bind_name other, %other.loc7_20.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness (%Op) [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Op = %Op +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Op.1[@impl.%self.loc7_9.2: i32](@impl.%other.loc7_20.2: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Op.2[%self: Self](%other: Self) -> Self; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_26: i32 = int_literal 3 [template = constants.%.11] +// CHECK:STDOUT: %.loc10_29: i32 = int_literal 4 [template = constants.%.12] +// CHECK:STDOUT: %.loc10_32: i32 = int_literal 3 [template = constants.%.11] +// CHECK:STDOUT: %.loc10_36: i32 = int_literal 4 [template = constants.%.12] +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%Op] +// CHECK:STDOUT: %.loc10_34.1: = bound_method %.loc10_32, %.1 [template = constants.%.13] +// CHECK:STDOUT: %.loc10_34.2: init i32 = call %.loc10_34.1(%.loc10_32, %.loc10_36) [template = constants.%.14] +// CHECK:STDOUT: %.loc10_37.1: (i32, i32, i32) = tuple_literal (%.loc10_26, %.loc10_29, %.loc10_34.2) +// CHECK:STDOUT: %.loc10_37.2: i32 = int_literal 0 [template = constants.%.16] +// CHECK:STDOUT: %.loc10_37.3: ref i32 = array_index file.%arr.var, %.loc10_37.2 +// CHECK:STDOUT: %.loc10_37.4: init i32 = initialize_from %.loc10_26 to %.loc10_37.3 [template = constants.%.11] +// CHECK:STDOUT: %.loc10_37.5: i32 = int_literal 1 [template = constants.%.17] +// CHECK:STDOUT: %.loc10_37.6: ref i32 = array_index file.%arr.var, %.loc10_37.5 +// CHECK:STDOUT: %.loc10_37.7: init i32 = initialize_from %.loc10_29 to %.loc10_37.6 [template = constants.%.12] +// CHECK:STDOUT: %.loc10_37.8: i32 = int_literal 2 [template = constants.%.18] +// CHECK:STDOUT: %.loc10_37.9: ref i32 = array_index file.%arr.var, %.loc10_37.8 +// CHECK:STDOUT: %.loc10_37.10: init i32 = initialize_from %.loc10_34.2 to %.loc10_37.9 [template = constants.%.14] +// CHECK:STDOUT: %.loc10_37.11: init [i32; 3] = array_init (%.loc10_37.4, %.loc10_37.7, %.loc10_37.10) to file.%arr.var [template = constants.%.19] +// CHECK:STDOUT: %.loc10_37.12: init [i32; 3] = converted %.loc10_37.1, %.loc10_37.11 [template = constants.%.19] +// CHECK:STDOUT: assign file.%arr.var, %.loc10_37.12 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/definition.carbon b/toolchain/check/testdata/function/builtin/definition.carbon new file mode 100644 index 000000000000..2341d0f86f89 --- /dev/null +++ b/toolchain/check/testdata/function/builtin/definition.carbon @@ -0,0 +1,25 @@ +// 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 Add(a: i32, b: i32) -> i32 = "int.add"; + +// CHECK:STDOUT: --- definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add: = fn_decl @Add [template] { +// CHECK:STDOUT: %a.loc7_8.1: i32 = param a +// CHECK:STDOUT: @Add.%a: i32 = bind_name a, %a.loc7_8.1 +// CHECK:STDOUT: %b.loc7_16.1: i32 = param b +// CHECK:STDOUT: @Add.%b: i32 = bind_name b, %b.loc7_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/fail_redefined.carbon b/toolchain/check/testdata/function/builtin/fail_redefined.carbon new file mode 100644 index 000000000000..e3aab4838734 --- /dev/null +++ b/toolchain/check/testdata/function/builtin/fail_redefined.carbon @@ -0,0 +1,99 @@ +// 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 A(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: fail_redefined.carbon:[[@LINE+6]]:1: ERROR: Redefinition of function A. +// CHECK:STDERR: fn A(n: i32, m: i32) -> i32 { return n; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: Previously defined here. +// CHECK:STDERR: fn A(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fn A(n: i32, m: i32) -> i32 { return n; } + +fn B(n: i32, m: i32) -> i32 { return n; } +// CHECK:STDERR: fail_redefined.carbon:[[@LINE+6]]:1: ERROR: Redefinition of function B. +// CHECK:STDERR: fn B(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: Previously defined here. +// CHECK:STDERR: fn B(n: i32, m: i32) -> i32 { return n; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fn B(n: i32, m: i32) -> i32 = "int.add"; + +fn C(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: fail_redefined.carbon:[[@LINE+6]]:1: ERROR: Redefinition of function C. +// CHECK:STDERR: fn C(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: Previously defined here. +// CHECK:STDERR: fn C(n: i32, m: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fn C(n: i32, m: i32) -> i32 = "int.add"; + +// CHECK:STDOUT: --- fail_redefined.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.loc7 +// CHECK:STDOUT: .B = %B.loc16 +// CHECK:STDOUT: .C = %C.loc25 +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.loc7: = fn_decl @A [template] { +// CHECK:STDOUT: %n.loc7_6.1: i32 = param n +// CHECK:STDOUT: @A.%n: i32 = bind_name n, %n.loc7_6.1 +// CHECK:STDOUT: %m.loc7_14.1: i32 = param m +// CHECK:STDOUT: @A.%m: i32 = bind_name m, %m.loc7_14.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.loc14: = fn_decl @A [template] { +// CHECK:STDOUT: %n.loc14_6.1: i32 = param n +// CHECK:STDOUT: %n.loc14_6.2: i32 = bind_name n, %n.loc14_6.1 +// CHECK:STDOUT: %m.loc14_14.1: i32 = param m +// CHECK:STDOUT: %m.loc14_14.2: i32 = bind_name m, %m.loc14_14.1 +// CHECK:STDOUT: %return.var.loc14: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.loc16: = fn_decl @B [template] { +// CHECK:STDOUT: %n.loc16_6.1: i32 = param n +// CHECK:STDOUT: @B.%n: i32 = bind_name n, %n.loc16_6.1 +// CHECK:STDOUT: %m.loc16_14.1: i32 = param m +// CHECK:STDOUT: @B.%m: i32 = bind_name m, %m.loc16_14.1 +// CHECK:STDOUT: %return.var.loc16: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.loc23: = fn_decl @B [template] { +// CHECK:STDOUT: %n.loc23_6.1: i32 = param n +// CHECK:STDOUT: %n.loc23_6.2: i32 = bind_name n, %n.loc23_6.1 +// CHECK:STDOUT: %m.loc23_14.1: i32 = param m +// CHECK:STDOUT: %m.loc23_14.2: i32 = bind_name m, %m.loc23_14.1 +// CHECK:STDOUT: %return.var.loc23: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.loc25: = fn_decl @C [template] { +// CHECK:STDOUT: %n.loc25_6.1: i32 = param n +// CHECK:STDOUT: @C.%n: i32 = bind_name n, %n.loc25_6.1 +// CHECK:STDOUT: %m.loc25_14.1: i32 = param m +// CHECK:STDOUT: @C.%m: i32 = bind_name m, %m.loc25_14.1 +// CHECK:STDOUT: %return.var.loc25: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.loc32: = fn_decl @C [template] { +// CHECK:STDOUT: %n.loc32_6.1: i32 = param n +// CHECK:STDOUT: %n.loc32_6.2: i32 = bind_name n, %n.loc32_6.1 +// CHECK:STDOUT: %m.loc32_14.1: i32 = param m +// CHECK:STDOUT: %m.loc32_14.2: i32 = bind_name m, %m.loc32_14.1 +// CHECK:STDOUT: %return.var.loc32: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(%n: i32, %m: i32) -> i32 = "int.add" { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: i32 = name_ref n, file.%n.loc14_6.2 +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(%n: i32, %m: i32) -> i32 = "int.add" { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n +// CHECK:STDOUT: return %n.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(%n: i32, %m: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/fail_unknown.carbon b/toolchain/check/testdata/function/builtin/fail_unknown.carbon new file mode 100644 index 000000000000..360a1f67eb7f --- /dev/null +++ b/toolchain/check/testdata/function/builtin/fail_unknown.carbon @@ -0,0 +1,22 @@ +// 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 + +// CHECK:STDERR: fail_unknown.carbon:[[@LINE+3]]:23: ERROR: Unknown builtin function name "unknown.builtin.name". +// CHECK:STDERR: fn UnknownBuiltin() = "unknown.builtin.name"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ +fn UnknownBuiltin() = "unknown.builtin.name"; + +// CHECK:STDOUT: --- fail_unknown.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UnknownBuiltin = %UnknownBuiltin +// CHECK:STDOUT: } +// CHECK:STDOUT: %UnknownBuiltin: = fn_decl @UnknownBuiltin [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @UnknownBuiltin(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/import.carbon b/toolchain/check/testdata/function/builtin/import.carbon new file mode 100644 index 000000000000..0a6db05957e0 --- /dev/null +++ b/toolchain/check/testdata/function/builtin/import.carbon @@ -0,0 +1,91 @@ +// 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 + +// --- core.carbon + +package Core api; + +fn Add(a: i32, b: i32) -> i32 = "int.add"; + +// --- use.carbon + +import Core; + +var arr: [i32; Core.Add(1, 2)] = (1, 2, 3); + +// CHECK:STDOUT: --- core.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Add = %Add +// CHECK:STDOUT: } +// CHECK:STDOUT: %Add: = fn_decl @Add [template] { +// CHECK:STDOUT: %a.loc4_8.1: i32 = param a +// CHECK:STDOUT: @Add.%a: i32 = bind_name a, %a.loc4_8.1 +// CHECK:STDOUT: %b.loc4_16.1: i32 = param b +// CHECK:STDOUT: @Add.%b: i32 = bind_name b, %b.loc4_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- use.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.3: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.4: type = array_type %.3, i32 [template] +// CHECK:STDOUT: %.5: type = ptr_type [i32; 3] [template] +// CHECK:STDOUT: %.6: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.7: type = tuple_type (i32, i32, i32) [template] +// CHECK:STDOUT: %.8: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.9: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.10: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.11: [i32; 3] = tuple_value (%.1, %.2, %.6) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Core.ref: = name_ref Core, %Core [template = %Core] +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+6, used [template = imports.%Add] +// CHECK:STDOUT: %Add.ref: = name_ref Add, %import_ref [template = imports.%Add] +// CHECK:STDOUT: %.loc4_25: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_28: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_24: init i32 = call %Add.ref(%.loc4_25, %.loc4_28) [template = constants.%.3] +// CHECK:STDOUT: %.loc4_30: type = array_type %.loc4_24, i32 [template = constants.%.4] +// CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr +// CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc4_35: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_38: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_41: i32 = int_literal 3 [template = constants.%.6] +// CHECK:STDOUT: %.loc4_42.1: (i32, i32, i32) = tuple_literal (%.loc4_35, %.loc4_38, %.loc4_41) +// CHECK:STDOUT: %.loc4_42.2: i32 = int_literal 0 [template = constants.%.8] +// CHECK:STDOUT: %.loc4_42.3: ref i32 = array_index file.%arr.var, %.loc4_42.2 +// CHECK:STDOUT: %.loc4_42.4: init i32 = initialize_from %.loc4_35 to %.loc4_42.3 [template = constants.%.1] +// CHECK:STDOUT: %.loc4_42.5: i32 = int_literal 1 [template = constants.%.9] +// CHECK:STDOUT: %.loc4_42.6: ref i32 = array_index file.%arr.var, %.loc4_42.5 +// CHECK:STDOUT: %.loc4_42.7: init i32 = initialize_from %.loc4_38 to %.loc4_42.6 [template = constants.%.2] +// CHECK:STDOUT: %.loc4_42.8: i32 = int_literal 2 [template = constants.%.10] +// CHECK:STDOUT: %.loc4_42.9: ref i32 = array_index file.%arr.var, %.loc4_42.8 +// CHECK:STDOUT: %.loc4_42.10: init i32 = initialize_from %.loc4_41 to %.loc4_42.9 [template = constants.%.6] +// CHECK:STDOUT: %.loc4_42.11: init [i32; 3] = array_init (%.loc4_42.4, %.loc4_42.7, %.loc4_42.10) to file.%arr.var [template = constants.%.11] +// CHECK:STDOUT: %.loc4_42.12: init [i32; 3] = converted %.loc4_42.1, %.loc4_42.11 [template = constants.%.11] +// CHECK:STDOUT: assign file.%arr.var, %.loc4_42.12 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/builtin/method.carbon b/toolchain/check/testdata/function/builtin/method.carbon new file mode 100644 index 000000000000..e27f0fe2c724 --- /dev/null +++ b/toolchain/check/testdata/function/builtin/method.carbon @@ -0,0 +1,97 @@ +// 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 + +interface I { + fn F[self: Self](other: Self) -> Self; +} + +impl i32 as I { + fn F[self: i32](other: i32) -> i32 = "int.add"; +} + +var arr: [i32; 1.(I.F)(2)]; + +// CHECK:STDOUT: --- method.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] +// CHECK:STDOUT: %.5: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.6: = bound_method %.5, @impl.%F [template] +// CHECK:STDOUT: %.7: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.8: i32 = int_literal 3 [template] +// CHECK:STDOUT: %.9: type = array_type %.8, i32 [template] +// CHECK:STDOUT: %.10: type = ptr_type [i32; 3] [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .arr = %arr +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %I.ref.loc11: type = name_ref I, %I.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc15_16: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %I.ref.loc15: type = name_ref I, %I.decl [template = constants.%.1] +// CHECK:STDOUT: %F.ref: in I> = name_ref F, @I.%.loc8_40 [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc15_17: = bound_method %.loc15_16, %.1 [template = constants.%.6] +// CHECK:STDOUT: %.loc15_24: i32 = int_literal 2 [template = constants.%.7] +// CHECK:STDOUT: %.loc15_23: init i32 = call %.loc15_17(%.loc15_16, %.loc15_24) [template = constants.%.8] +// CHECK:STDOUT: %.loc15_26: type = array_type %.loc15_23, i32 [template = constants.%.9] +// CHECK:STDOUT: %arr.var: ref [i32; 3] = var arr +// CHECK:STDOUT: %arr: ref [i32; 3] = bind_name arr, %arr.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] { +// CHECK:STDOUT: %Self.ref.loc8_14: I = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_14.1: type = facet_type_access %Self.ref.loc8_14 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_14.2: type = converted %Self.ref.loc8_14, %.loc8_14.1 [symbolic = %Self] +// CHECK:STDOUT: %self.loc8_8.1: Self = param self +// CHECK:STDOUT: %self.loc8_8.2: Self = bind_name self, %self.loc8_8.1 +// CHECK:STDOUT: %Self.ref.loc8_27: I = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_27.1: type = facet_type_access %Self.ref.loc8_27 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_27.2: type = converted %Self.ref.loc8_27, %.loc8_27.1 [symbolic = %Self] +// CHECK:STDOUT: %other.loc8_20.1: Self = param other +// CHECK:STDOUT: %other.loc8_20.2: Self = bind_name other, %other.loc8_20.1 +// CHECK:STDOUT: %Self.ref.loc8_36: I = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_36.1: type = facet_type_access %Self.ref.loc8_36 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_36.2: type = converted %Self.ref.loc8_36, %.loc8_36.1 [symbolic = %Self] +// CHECK:STDOUT: %return.var: ref Self = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_40: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8_40 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: i32 as I { +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %self.loc12_8.1: i32 = param self +// CHECK:STDOUT: %self.loc12_8.2: i32 = bind_name self, %self.loc12_8.1 +// CHECK:STDOUT: %other.loc12_19.1: i32 = param other +// CHECK:STDOUT: %other.loc12_19.2: i32 = bind_name other, %other.loc12_19.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1[@I.%self.loc8_8.2: Self](@I.%other.loc8_20.2: Self) -> Self; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2[@impl.%self.loc12_8.2: i32](@impl.%other.loc12_19.2: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/call/fail_param_count.carbon b/toolchain/check/testdata/function/call/fail_param_count.carbon index 5017ebb0e05f..e41c84a747af 100644 --- a/toolchain/check/testdata/function/call/fail_param_count.carbon +++ b/toolchain/check/testdata/function/call/fail_param_count.carbon @@ -103,22 +103,22 @@ fn Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Run0.ref.loc18: = name_ref Run0, file.%Run0 [template = file.%Run0] // CHECK:STDOUT: %.loc18_8: i32 = int_literal 1 [template = constants.%.1] -// CHECK:STDOUT: %.loc18_7: init () = call %Run0.ref.loc18() +// CHECK:STDOUT: %.loc18_7: init () = call %Run0.ref.loc18() [template = ] // CHECK:STDOUT: %Run0.ref.loc25: = name_ref Run0, file.%Run0 [template = file.%Run0] // CHECK:STDOUT: %.loc25_8: i32 = int_literal 0 [template = constants.%.3] // CHECK:STDOUT: %.loc25_11: i32 = int_literal 1 [template = constants.%.1] -// CHECK:STDOUT: %.loc25_7: init () = call %Run0.ref.loc25() +// CHECK:STDOUT: %.loc25_7: init () = call %Run0.ref.loc25() [template = ] // CHECK:STDOUT: %Run1.ref.loc33: = name_ref Run1, file.%Run1 [template = file.%Run1] -// CHECK:STDOUT: %.loc33: init () = call %Run1.ref.loc33() +// CHECK:STDOUT: %.loc33: init () = call %Run1.ref.loc33() [template = ] // CHECK:STDOUT: %Run1.ref.loc40: = name_ref Run1, file.%Run1 [template = file.%Run1] // CHECK:STDOUT: %.loc40_8: i32 = int_literal 0 [template = constants.%.3] // CHECK:STDOUT: %.loc40_11: i32 = int_literal 1 [template = constants.%.1] -// CHECK:STDOUT: %.loc40_7: init () = call %Run1.ref.loc40() +// CHECK:STDOUT: %.loc40_7: init () = call %Run1.ref.loc40() [template = ] // CHECK:STDOUT: %Run2.ref.loc48: = name_ref Run2, file.%Run2 [template = file.%Run2] -// CHECK:STDOUT: %.loc48: init () = call %Run2.ref.loc48() +// CHECK:STDOUT: %.loc48: init () = call %Run2.ref.loc48() [template = ] // CHECK:STDOUT: %Run2.ref.loc55: = name_ref Run2, file.%Run2 [template = file.%Run2] // CHECK:STDOUT: %.loc55_8: i32 = int_literal 0 [template = constants.%.3] -// CHECK:STDOUT: %.loc55_7: init () = call %Run2.ref.loc55() +// CHECK:STDOUT: %.loc55_7: init () = call %Run2.ref.loc55() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/call/fail_param_type.carbon b/toolchain/check/testdata/function/call/fail_param_type.carbon index 0844d371314c..923a373e6fd6 100644 --- a/toolchain/check/testdata/function/call/fail_param_type.carbon +++ b/toolchain/check/testdata/function/call/fail_param_type.carbon @@ -44,7 +44,7 @@ fn F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: = name_ref G, file.%G [template = file.%G] // CHECK:STDOUT: %.loc16_5: f64 = real_literal 10e-1 [template = constants.%.1] -// CHECK:STDOUT: %.loc16_4: init () = call %G.ref() +// CHECK:STDOUT: %.loc16_4: init () = call %G.ref() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon b/toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon similarity index 52% rename from toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon rename to toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon index 806f62a0042e..1ba464301c2f 100644 --- a/toolchain/check/testdata/interface/fail_todo_define_out_of_line.carbon +++ b/toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon @@ -5,26 +5,24 @@ // AUTOUPDATE interface Interface { - // CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. - // CHECK:STDERR: default fn F(); + // CHECK:STDERR: fail_todo_define_default_fn_inline.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn F() {} // CHECK:STDERR: ^~~~~~~ - default fn F(); + default fn F() {} + + // CHECK:STDERR: fail_todo_define_default_fn_inline.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn G(a: i32, b: i32) -> i32 = "int.add"; + // CHECK:STDERR: ^~~~~~~ + default fn G(a: i32, b: i32) -> i32 = "int.add"; } -// CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn Interface.F() {} -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_define_out_of_line.carbon:[[@LINE-6]]:3: Name is previously declared here. -// CHECK:STDERR: default fn F(); -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -fn Interface.F() {} - -// CHECK:STDOUT: --- fail_todo_define_out_of_line.carbon +// CHECK:STDOUT: --- fail_todo_define_default_fn_inline.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Interface [template] // CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] // CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] +// CHECK:STDOUT: %.4: in Interface> = assoc_entity element1, @Interface.%G [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -32,24 +30,32 @@ fn Interface.F() {} // CHECK:STDOUT: .Interface = %Interface.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Interface.decl: type = interface_decl @Interface [template = constants.%.1] {} -// CHECK:STDOUT: %.loc20: = fn_decl @.1 [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Interface { // CHECK:STDOUT: %Self: Interface = bind_symbolic_name Self [symbolic] // CHECK:STDOUT: %F: = fn_decl @F [template] {} // CHECK:STDOUT: %.loc11: in Interface> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %a.loc16_16.1: i32 = param a +// CHECK:STDOUT: %a.loc16_16.2: i32 = bind_name a, %a.loc16_16.1 +// CHECK:STDOUT: %b.loc16_24.1: i32 = param b +// CHECK:STDOUT: %b.loc16_24.2: i32 = bind_name b, %b.loc16_24.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc16: in Interface> = assoc_entity element1, %G [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = %.loc11 -// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: .G = %.loc16 +// CHECK:STDOUT: witness = (%F, %G) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @.1() { +// CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @G(@Interface.%a.loc16_16.2: i32, @Interface.%b.loc16_24.2: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon b/toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon new file mode 100644 index 000000000000..cf3b12fbb3a3 --- /dev/null +++ b/toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon @@ -0,0 +1,89 @@ +// 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 + +interface Interface { + // CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn F(); + // CHECK:STDERR: ^~~~~~~ + default fn F(); + + // CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `interface modifier`. + // CHECK:STDERR: default fn G(a: i32, b: i32) -> i32; + // CHECK:STDERR: ^~~~~~~ + default fn G(a: i32, b: i32) -> i32; +} + +// CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fn Interface.F() {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE-11]]:3: Name is previously declared here. +// CHECK:STDERR: default fn F(); +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +fn Interface.F() {} + +// CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fn Interface.G(a: i32, b: i32) -> i32 = "int.add"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_todo_define_default_fn_out_of_line.carbon:[[@LINE-14]]:3: Name is previously declared here. +// CHECK:STDERR: default fn G(a: i32, b: i32) -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fn Interface.G(a: i32, b: i32) -> i32 = "int.add"; + +// CHECK:STDOUT: --- fail_todo_define_default_fn_out_of_line.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @Interface [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @Interface, [template] +// CHECK:STDOUT: %.3: in Interface> = assoc_entity element0, @Interface.%F [template] +// CHECK:STDOUT: %.4: in Interface> = assoc_entity element1, @Interface.%G [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Interface = %Interface.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Interface.decl: type = interface_decl @Interface [template = constants.%.1] {} +// CHECK:STDOUT: %.loc25: = fn_decl @.1 [template] {} +// CHECK:STDOUT: %.loc33: = fn_decl @.2 [template] { +// CHECK:STDOUT: %a.loc33_16.1: i32 = param a +// CHECK:STDOUT: @.2.%a: i32 = bind_name a, %a.loc33_16.1 +// CHECK:STDOUT: %b.loc33_24.1: i32 = param b +// CHECK:STDOUT: @.2.%b: i32 = bind_name b, %b.loc33_24.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Interface { +// CHECK:STDOUT: %Self: Interface = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F [template] {} +// CHECK:STDOUT: %.loc11: in Interface> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %a.loc16_16.1: i32 = param a +// CHECK:STDOUT: %a.loc16_16.2: i32 = bind_name a, %a.loc16_16.1 +// CHECK:STDOUT: %b.loc16_24.1: i32 = param b +// CHECK:STDOUT: %b.loc16_24.2: i32 = bind_name b, %b.loc16_24.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc16: in Interface> = assoc_entity element1, %G [template = constants.%.4] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc11 +// CHECK:STDOUT: .G = %.loc16 +// CHECK:STDOUT: witness = (%F, %G) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(@Interface.%a.loc16_16.2: i32, @Interface.%b.loc16_24.2: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.1() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.2(%a: i32, %b: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/todo_define_not_default.carbon b/toolchain/check/testdata/interface/todo_define_not_default.carbon new file mode 100644 index 000000000000..08f32b45bf44 --- /dev/null +++ b/toolchain/check/testdata/interface/todo_define_not_default.carbon @@ -0,0 +1,76 @@ +// 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 + +interface I { + // TODO: A definition without `default` in an interface should be rejected. + fn F() {} + fn G(a: i32, b: i32) -> i32 = "int.add"; + + // TODO: An associated constant with an initializer without `default` in an + // interface should be rejected. + let T:! type = (i32, i32); + let N:! i32 = 42; +} + +// CHECK:STDOUT: --- todo_define_not_default.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %.4: in I> = assoc_entity element1, @I.%G [template] +// CHECK:STDOUT: %.5: type = tuple_type (type, type) [template] +// CHECK:STDOUT: %.6: type = tuple_type (i32, i32) [template] +// CHECK:STDOUT: %.7: type = assoc_entity_type @I, type [template] +// CHECK:STDOUT: %.8: = assoc_entity element2, @I.%T [template] +// CHECK:STDOUT: %.9: i32 = int_literal 42 [template] +// CHECK:STDOUT: %.10: type = assoc_entity_type @I, i32 [template] +// CHECK:STDOUT: %.11: = assoc_entity element3, @I.%N [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F [template] {} +// CHECK:STDOUT: %.loc9: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %a.loc10_8.1: i32 = param a +// CHECK:STDOUT: %a.loc10_8.2: i32 = bind_name a, %a.loc10_8.1 +// CHECK:STDOUT: %b.loc10_16.1: i32 = param b +// CHECK:STDOUT: %b.loc10_16.2: i32 = bind_name b, %b.loc10_16.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10: in I> = assoc_entity element1, %G [template = constants.%.4] +// CHECK:STDOUT: %.loc14_27.1: (type, type) = tuple_literal (i32, i32) +// CHECK:STDOUT: %.loc14_27.2: type = converted %.loc14_27.1, constants.%.6 [template = constants.%.6] +// CHECK:STDOUT: %T: type = assoc_const_decl T [template] +// CHECK:STDOUT: %.loc14_28: = assoc_entity element2, %T [template = constants.%.8] +// CHECK:STDOUT: %.loc15_17: i32 = int_literal 42 [template = constants.%.9] +// CHECK:STDOUT: %N: i32 = assoc_const_decl N [template] +// CHECK:STDOUT: %.loc15_19: = assoc_entity element3, %N [template = constants.%.11] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc9 +// CHECK:STDOUT: .G = %.loc10 +// CHECK:STDOUT: .T = %.loc14_28 +// CHECK:STDOUT: .N = %.loc15_19 +// CHECK:STDOUT: witness = (%F, %G, %T, %N) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(@I.%a.loc10_8.2: i32, @I.%b.loc10_16.2: i32) -> i32 = "int.add"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/eq.carbon b/toolchain/check/testdata/operators/overloaded/eq.carbon index 964293c98d83..81da7cf1eecd 100644 --- a/toolchain/check/testdata/operators/overloaded/eq.carbon +++ b/toolchain/check/testdata/operators/overloaded/eq.carbon @@ -476,9 +476,9 @@ fn TestLhsBad(a: D, b: C) -> bool { // CHECK:STDOUT: %b.ref: D = name_ref b, %b // CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%Equal] // CHECK:STDOUT: %.loc21_12.1: = bound_method %a.ref, %.1 -// CHECK:STDOUT: %.loc21_12.2: init bool = call %.loc21_12.1() -// CHECK:STDOUT: %.loc21_16: bool = value_of_initializer %.loc21_12.2 -// CHECK:STDOUT: %.loc21_12.3: bool = converted %.loc21_12.2, %.loc21_16 +// CHECK:STDOUT: %.loc21_12.2: init bool = call %.loc21_12.1() [template = ] +// CHECK:STDOUT: %.loc21_16: bool = value_of_initializer %.loc21_12.2 [template = ] +// CHECK:STDOUT: %.loc21_12.3: bool = converted %.loc21_12.2, %.loc21_16 [template = ] // CHECK:STDOUT: return %.loc21_12.3 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon b/toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon index 32567cc8f558..ab77882e682b 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon @@ -252,7 +252,7 @@ fn TestAddAssignNonRef(a: C, b: C) { // CHECK:STDOUT: %a.ref: C = name_ref a, %a // CHECK:STDOUT: %.1: = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op] // CHECK:STDOUT: %.loc22_3.1: = bound_method %a.ref, %.1 -// CHECK:STDOUT: %.loc22_3.2: init () = call %.loc22_3.1() +// CHECK:STDOUT: %.loc22_3.2: init () = call %.loc22_3.1() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -262,7 +262,7 @@ fn TestAddAssignNonRef(a: C, b: C) { // CHECK:STDOUT: %b.ref: C = name_ref b, %b // CHECK:STDOUT: %.1: = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op] // CHECK:STDOUT: %.loc32_5.1: = bound_method %a.ref, %.1 -// CHECK:STDOUT: %.loc32_5.2: init () = call %.loc32_5.1() +// CHECK:STDOUT: %.loc32_5.2: init () = call %.loc32_5.1() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon index aa66a33cf5b3..8973b594b75f 100644 --- a/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon +++ b/toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon @@ -275,7 +275,7 @@ fn TestAssign(b: D) { // CHECK:STDOUT: %.1: = interface_witness_access @impl.1.%.1, element0 [template = @impl.1.%Op] // CHECK:STDOUT: %.loc23_12.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc23_12.2: ref C = temporary_storage -// CHECK:STDOUT: %.loc23_12.3: init C = call %.loc23_12.1() +// CHECK:STDOUT: %.loc23_12.3: init C = call %.loc23_12.1() [template = ] // CHECK:STDOUT: return %.loc23_12.3 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -293,7 +293,7 @@ fn TestAssign(b: D) { // CHECK:STDOUT: %.1: = interface_witness_access @impl.2.%.1, element0 [template = @impl.2.%Op] // CHECK:STDOUT: %.loc34_5.1: = bound_method %a.ref, %.1 // CHECK:STDOUT: %.loc34_3: C* = addr_of %a.ref -// CHECK:STDOUT: %.loc34_5.2: init () = call %.loc34_5.1() +// CHECK:STDOUT: %.loc34_5.2: init () = call %.loc34_5.1() [template = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 75236a4a366d..a187d8304cc5 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -114,6 +114,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedDeclSemiOrDefinition) CARBON_DIAGNOSTIC_KIND(ParamsRequiredAfterImplicit) CARBON_DIAGNOSTIC_KIND(ParamsRequiredByIntroducer) CARBON_DIAGNOSTIC_KIND(ExpectedAfterBase) +CARBON_DIAGNOSTIC_KIND(ExpectedBuiltinName) CARBON_DIAGNOSTIC_KIND(ImplExpectedAfterForall) CARBON_DIAGNOSTIC_KIND(ImplExpectedAs) @@ -170,6 +171,7 @@ CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypePrevious) CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypePreviousNoReturn) CARBON_DIAGNOSTIC_KIND(InvalidMainRunSignature) CARBON_DIAGNOSTIC_KIND(MissingReturnStatement) +CARBON_DIAGNOSTIC_KIND(UnknownBuiltinFunctionName) // Class checking. CARBON_DIAGNOSTIC_KIND(BaseIsFinal) @@ -232,6 +234,7 @@ CARBON_DIAGNOSTIC_KIND(ArrayInitFromExprArgCountMismatch) CARBON_DIAGNOSTIC_KIND(ArrowOperatorOfNonPointer) CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable) CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop) +CARBON_DIAGNOSTIC_KIND(CompileTimeIntegerOverflow) CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop) CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType) CARBON_DIAGNOSTIC_KIND(DerefOfNonPointer) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 43f839291d7a..afcc31f75934 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -10,6 +10,7 @@ #include "toolchain/lower/function_context.h" #include "toolchain/sem_ir/entry_point.h" #include "toolchain/sem_ir/file.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -95,6 +96,11 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) return nullptr; } + // Don't lower builtins. + if (function.builtin_kind != SemIR::BuiltinFunctionKind::None) { + return nullptr; + } + const bool has_return_slot = function.return_slot_id.is_valid(); auto implicit_param_refs = sem_ir().inst_blocks().Get(function.implicit_param_refs_id); diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index e05ab053641d..f769df834b0d 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -23,9 +23,8 @@ class FileContext { // the main execution loop. auto Run() -> std::unique_ptr; - // Gets a callable's function. + // Gets a callable's function. Returns nullptr for a builtin. auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* { - CARBON_CHECK(functions_[function_id.index] != nullptr) << function_id; return functions_[function_id.index]; } diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 958160de137e..75293adde7df 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -11,6 +11,7 @@ #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "toolchain/lower/function_context.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -166,14 +167,79 @@ auto HandleBuiltin(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, CARBON_FATAL() << "TODO: Add support: " << inst; } +// Returns the builtin function kind of the callee in a function call, or None +// if the call is not to a builtin. +static auto GetCalleeBuiltinFunctionKind(const SemIR::File& sem_ir, + SemIR::InstId callee_id) + -> SemIR::BuiltinFunctionKind { + if (auto bound_method = + sem_ir.insts().TryGetAs(callee_id)) { + callee_id = bound_method->function_id; + } + callee_id = sem_ir.constant_values().Get(callee_id).inst_id(); + if (!callee_id.is_valid()) { + return SemIR::BuiltinFunctionKind::None; + } + if (auto callee = sem_ir.insts().TryGetAs(callee_id)) { + const auto& function = sem_ir.functions().Get(callee->function_id); + return function.builtin_kind; + } + return SemIR::BuiltinFunctionKind::None; +} + +// Handles a call to a builtin function. +static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, + SemIR::BuiltinFunctionKind builtin_kind, + llvm::ArrayRef arg_ids) -> void { + switch (builtin_kind) { + case SemIR::BuiltinFunctionKind::None: + CARBON_FATAL() << "No callee in function call."; + + case SemIR::BuiltinFunctionKind::IntAdd: { + // TODO: Move type checking to the point where we make the call. + if (arg_ids.size() != 2) { + break; + } + auto lhs_type = context.sem_ir().insts().Get(arg_ids[0]).type_id(); + auto rhs_type = context.sem_ir().insts().Get(arg_ids[1]).type_id(); + auto result_type = context.sem_ir().insts().Get(inst_id).type_id(); + if (lhs_type != rhs_type || lhs_type != result_type || + context.sem_ir().types().GetInstId(lhs_type) != + SemIR::InstId::BuiltinIntType) { + break; + } + constexpr bool SignedOverflowIsUB = false; + context.SetLocal(inst_id, context.builder().CreateAdd( + context.GetValue(arg_ids[0]), + context.GetValue(arg_ids[1]), "add", + /*HasNUW=*/false, + /*HasNSW=*/SignedOverflowIsUB)); + return; + } + } + + CARBON_FATAL() << "Unsupported builtin call."; +} + auto HandleCall(FunctionContext& context, SemIR::InstId inst_id, SemIR::Call inst) -> void { - auto* callee = llvm::cast(context.GetValue(inst.callee_id)); - - std::vector args; llvm::ArrayRef arg_ids = context.sem_ir().inst_blocks().Get(inst.args_id); + auto* callee_value = context.GetValue(inst.callee_id); + + // A null callee pointer value indicates this isn't a real function. + if (!callee_value) { + auto builtin_kind = + GetCalleeBuiltinFunctionKind(context.sem_ir(), inst.callee_id); + HandleBuiltinCall(context, inst_id, builtin_kind, arg_ids); + return; + } + + auto* callee = llvm::cast(callee_value); + + std::vector args; + if (SemIR::GetInitRepr(context.sem_ir(), inst.type_id).has_return_slot()) { args.push_back(context.GetValue(arg_ids.back())); arg_ids = arg_ids.drop_back(); diff --git a/toolchain/lower/testdata/builtins/int.carbon b/toolchain/lower/testdata/builtins/int.carbon new file mode 100644 index 000000000000..706cc8477e76 --- /dev/null +++ b/toolchain/lower/testdata/builtins/int.carbon @@ -0,0 +1,16 @@ +// 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 Add(a: i32, b: i32) -> i32 = "int.add"; +fn TestAdd(a: i32, b: i32) -> i32 { return Add(a, b); } + +// CHECK:STDOUT: ; ModuleID = 'int.carbon' +// CHECK:STDOUT: source_filename = "int.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @TestAdd(i32 %a, i32 %b) { +// CHECK:STDOUT: %add = add i32 %a, %b +// CHECK:STDOUT: ret i32 %add +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/builtins/method_vs_nonmethod.carbon b/toolchain/lower/testdata/builtins/method_vs_nonmethod.carbon new file mode 100644 index 000000000000..00d7370f5a62 --- /dev/null +++ b/toolchain/lower/testdata/builtins/method_vs_nonmethod.carbon @@ -0,0 +1,24 @@ +// 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 AddNonmethod(a: i32, b: i32) -> i32 = "int.add"; +fn AddMethod[self: i32](b: i32) -> i32 = "int.add"; + +fn TestAddNonmethod(a: i32, b: i32) -> i32 { return AddNonmethod(a, b); } +fn TestAddMethod(a: i32, b: i32) -> i32 { return a.(AddMethod)(b); } + +// CHECK:STDOUT: ; ModuleID = 'method_vs_nonmethod.carbon' +// CHECK:STDOUT: source_filename = "method_vs_nonmethod.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @TestAddNonmethod(i32 %a, i32 %b) { +// CHECK:STDOUT: %add = add i32 %a, %b +// CHECK:STDOUT: ret i32 %add +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @TestAddMethod(i32 %a, i32 %b) { +// CHECK:STDOUT: %add = add i32 %a, %b +// CHECK:STDOUT: ret i32 %add +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/builtins/overloaded_operator.carbon b/toolchain/lower/testdata/builtins/overloaded_operator.carbon new file mode 100644 index 000000000000..2e021db912f5 --- /dev/null +++ b/toolchain/lower/testdata/builtins/overloaded_operator.carbon @@ -0,0 +1,41 @@ +// 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 + +// --- prelude.carbon + +package Core api; + +interface Add { + fn Op[self: Self](other: Self) -> Self; +} + +// --- user.carbon + +import Core; + +// TODO: Move this into prelude.carbon when possible. For now, impl lookup only +// looks in the current file. +impl i32 as Core.Add { + fn Op[self: i32](other: i32) -> i32 = "int.add"; +} + +fn AddThreeIntegers(a: i32, b: i32, c: i32) -> i32 { + return a + b + c; +} + +// CHECK:STDOUT: ; ModuleID = 'prelude.carbon' +// CHECK:STDOUT: source_filename = "prelude.carbon" +// CHECK:STDOUT: ; ModuleID = 'user.carbon' +// CHECK:STDOUT: source_filename = "user.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @AddThreeIntegers(i32 %a, i32 %b, i32 %c) { +// CHECK:STDOUT: %add = add i32 %a, %b +// CHECK:STDOUT: %temp = alloca i32, align 4 +// CHECK:STDOUT: store i32 %add, ptr %temp, align 4 +// CHECK:STDOUT: %1 = load i32, ptr %temp, align 4 +// CHECK:STDOUT: %add1 = add i32 %1, %c +// CHECK:STDOUT: ret i32 %add1 +// CHECK:STDOUT: } diff --git a/toolchain/parse/extract.cpp b/toolchain/parse/extract.cpp index c4a450d7bef3..a8ba7e837bf3 100644 --- a/toolchain/parse/extract.cpp +++ b/toolchain/parse/extract.cpp @@ -128,39 +128,48 @@ struct Extractable> { } }; -static auto NodeIdForKindAccept(NodeKind kind1, NodeKind kind2, - const Tree* tree, - const Tree::SiblingIterator& it, - Tree::SiblingIterator end, ErrorBuilder* trace) +static auto NodeIdOneOfAccept(std::initializer_list kinds, + const Tree* tree, const Tree::SiblingIterator& it, + Tree::SiblingIterator end, ErrorBuilder* trace) -> bool { + auto trace_kinds = [&] { + llvm::ListSeparator sep(" or "); + for (auto kind : kinds) { + *trace << sep << kind; + } + }; auto kind = tree->node_kind(*it); - if (it == end || (kind != kind1 && kind != kind2)) { + if (it == end || std::find(kinds.begin(), kinds.end(), kind) == kinds.end()) { if (trace) { if (it == end) { - *trace << "NodeIdOneOf error: no more children, expected " << kind1 - << " or " << kind2 << "\n"; + *trace << "NodeIdOneOf error: no more children, expected "; + trace_kinds(); + *trace << "\n"; } else { *trace << "NodeIdOneOf error: wrong kind " << tree->node_kind(*it) - << ", expected " << kind1 << " or " << kind2 << "\n"; + << ", expected "; + trace_kinds(); + *trace << "\n"; } } return false; } if (trace) { - *trace << "NodeIdOneOf " << kind1 << " or " << kind2 << ": " - << tree->node_kind(*it) << " consumed\n"; + *trace << "NodeIdOneOf "; + trace_kinds(); + *trace << ": " << tree->node_kind(*it) << " consumed\n"; } return true; } -// Extract a `NodeIdOneOf` as a single required child. -template -struct Extractable> { +// Extract a `NodeIdOneOf` as a single required child. +template +struct Extractable> { static auto Extract(const Tree* tree, Tree::SiblingIterator& it, Tree::SiblingIterator end, ErrorBuilder* trace) - -> std::optional> { - if (NodeIdForKindAccept(T::Kind, U::Kind, tree, it, end, trace)) { - return NodeIdOneOf(*it++); + -> std::optional> { + if (NodeIdOneOfAccept({T::Kind...}, tree, it, end, trace)) { + return NodeIdOneOf(*it++); } else { return std::nullopt; } diff --git a/toolchain/parse/handle_function.cpp b/toolchain/parse/handle_function.cpp index d1d3dd607d63..7568abe1a38a 100644 --- a/toolchain/parse/handle_function.cpp +++ b/toolchain/parse/handle_function.cpp @@ -52,6 +52,30 @@ auto HandleFunctionSignatureFinish(Context& context) -> void { context.PushState(State::StatementScopeLoop); break; } + case Lex::TokenKind::Equal: { + context.AddNode(NodeKind::BuiltinFunctionDefinitionStart, + context.Consume(), state.subtree_start, state.has_error); + if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::StringLiteral, + NodeKind::BuiltinName)) { + CARBON_DIAGNOSTIC(ExpectedBuiltinName, Error, + "Expected builtin function name after `=`."); + context.emitter().Emit(*context.position(), ExpectedBuiltinName); + state.has_error = true; + } + auto semi = context.ConsumeIf(Lex::TokenKind::Semi); + if (!semi && !state.has_error) { + context.EmitExpectedDeclSemi(context.tokens().GetKind(state.token)); + state.has_error = true; + } + if (state.has_error) { + context.RecoverFromDeclError(state, NodeKind::BuiltinFunctionDefinition, + /*skip_past_likely_end=*/true); + } else { + context.AddNode(NodeKind::BuiltinFunctionDefinition, *semi, + state.subtree_start, state.has_error); + } + break; + } default: { if (!state.has_error) { context.EmitExpectedDeclSemiOrDefinition(Lex::TokenKind::Fn); diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 678b510ce56a..f455ffcdb752 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -77,14 +77,15 @@ using AnyNameComponentId = NodeIdInCategory; using AnyPatternId = NodeIdInCategory; using AnyStatementId = NodeIdInCategory; -// NodeId with kind that matches either T::Kind or U::Kind. -template +// NodeId with kind that matches one of the `T::Kind`s. +template struct NodeIdOneOf : public NodeId { + static_assert(sizeof...(T) >= 2, "Expected at least two types."); constexpr explicit NodeIdOneOf(NodeId node_id) : NodeId(node_id) {} template // NOLINTNEXTLINE(google-explicit-constructor) NodeIdOneOf(NodeIdForKind node_id) : NodeId(node_id) { - static_assert(T::Kind == Kind || U::Kind == Kind); + static_assert(((T::Kind == Kind) || ...)); } // NOLINTNEXTLINE(google-explicit-constructor) constexpr NodeIdOneOf(InvalidNodeId /*invalid*/) @@ -92,8 +93,8 @@ struct NodeIdOneOf : public NodeId { }; using AnyClassDeclId = NodeIdOneOf; -using AnyFunctionDeclId = - NodeIdOneOf; +using AnyFunctionDeclId = NodeIdOneOf; using AnyImplDeclId = NodeIdOneOf; using AnyInterfaceDeclId = NodeIdOneOf; diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 85c5c7213409..f09905415b3e 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -232,21 +232,31 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(CodeBlockStart, 0, CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart, CARBON_IF_VALID(CloseCurlyBrace)) -// `fn`: -// FunctionIntroducer -// _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName -// _optional_ _external_: ImplicitParamList -// _external_: TuplePattern -// _external_: type expression -// ReturnType +// `fn` declarations start with a function signature: +// +// FunctionIntroducer +// _repeated_ _external_: modifier +// _external_: IdentifierName or QualifiedName +// _optional_ _external_: ImplicitParamList +// _external_: TuplePattern +// _external_: type expression +// ReturnType +// _function signature_ +// +// There are three forms of function declaration: +// +// _function signature_ +// FunctionDecl +// +// _function signature_ // FunctionDefinitionStart // _repeated_ _external_: statement // FunctionDefinition // -// The above is the structure for a definition; for a declaration, -// FunctionDefinitionStart and later nodes are removed and replaced by -// FunctionDecl. +// _function signature_ +// BuiltinFunctionDefinitionStart +// BuiltinName +// BuiltinFunctionDefinition CARBON_PARSE_NODE_KIND_CHILD_COUNT(FunctionIntroducer, 0, Fn) CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnType, 1, MinusGreater) CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinitionStart, FunctionIntroducer, @@ -255,6 +265,12 @@ CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinition, FunctionDefinitionStart, CloseCurlyBrace) CARBON_PARSE_NODE_KIND_BRACKET(FunctionDecl, FunctionIntroducer, CARBON_IF_VALID(Semi)) +CARBON_PARSE_NODE_KIND_BRACKET(BuiltinFunctionDefinitionStart, + FunctionIntroducer, Equal) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(BuiltinName, 0, StringLiteral) +CARBON_PARSE_NODE_KIND_BRACKET(BuiltinFunctionDefinition, + BuiltinFunctionDefinitionStart, + CARBON_IF_VALID(Semi)) // `alias`: // AliasIntroducer diff --git a/toolchain/parse/testdata/function/definition/builtin.carbon b/toolchain/parse/testdata/function/definition/builtin.carbon new file mode 100644 index 000000000000..fbc52e6023ba --- /dev/null +++ b/toolchain/parse/testdata/function/definition/builtin.carbon @@ -0,0 +1,37 @@ +// 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() = "builtin"; + +impl T as I { + fn Op() = "builtin"; +} + +// CHECK:STDOUT: - filename: builtin.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BuiltinName', text: '"builtin"'}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinition', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ImplIntroducer', text: 'impl'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeImplAs', text: 'as', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Op'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BuiltinName', text: '"builtin"'}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinition', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ImplDefinition', text: '}', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/function/definition/fail_builtin.carbon b/toolchain/parse/testdata/function/definition/fail_builtin.carbon new file mode 100644 index 000000000000..1b639416b4af --- /dev/null +++ b/toolchain/parse/testdata/function/definition/fail_builtin.carbon @@ -0,0 +1,50 @@ +// 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 + +// CHECK:STDERR: fail_builtin.carbon:[[@LINE+3]]:18: ERROR: Expected builtin function name after `=`. +// CHECK:STDERR: fn NotString() = banana; +// CHECK:STDERR: ^~~~~~ +fn NotString() = banana; +// CHECK:STDERR: fail_builtin.carbon:[[@LINE+3]]:32: ERROR: `fn` declarations must end with a `;`. +// CHECK:STDERR: fn JunkAfterString() = "hello" "world"; +// CHECK:STDERR: ^~~~~~~ +fn JunkAfterString() = "hello" "world"; + +// CHECK:STDERR: fail_builtin.carbon:[[@LINE+3]]:23: ERROR: Expected builtin function name after `=`. +// CHECK:STDERR: fn SpuriousEquals() = { } +// CHECK:STDERR: ^ +fn SpuriousEquals() = { } +fn TestRecoveryFromSpuriousEquals(); + +// CHECK:STDOUT: - filename: fail_builtin.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'NotString'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinition', text: ';', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'JunkAfterString'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BuiltinName', text: '"hello"'}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinition', text: ';', has_error: yes, subtree_size: 7}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'SpuriousEquals'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BuiltinFunctionDefinition', text: '}', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'TestRecoveryFromSpuriousEquals'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index 5e5cf1c05285..e287f055a652 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -492,10 +492,10 @@ struct Tree::ConvertTo> { } }; -template -struct Tree::ConvertTo> { +template +struct Tree::ConvertTo> { static auto AllowedFor(NodeKind kind) -> bool { - return kind == T::Kind || kind == U::Kind; + return ((kind == T::Kind) || ...); } }; diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index a7d04cf73341..a1537f6310dd 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -312,6 +312,20 @@ struct FunctionDefinition { llvm::SmallVector body; }; +using BuiltinFunctionDefinitionStart = + FunctionSignature; +using BuiltinName = LeafNode; + +// A builtin function definition: `fn F() -> i32 = "builtin name";` +struct BuiltinFunctionDefinition { + static constexpr auto Kind = + NodeKind::BuiltinFunctionDefinition.Define(NodeCategory::Decl); + + BuiltinFunctionDefinitionStartId signature; + BuiltinNameId builtin_name; +}; + // `alias` nodes // ------------- diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 52d9044bdb3c..4a23f50eb95e 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -12,6 +12,7 @@ #include "toolchain/base/value_store.h" #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/tree.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -795,6 +796,15 @@ class Formatter { FormatType(fn.return_type_id); } + // TODO: Move this conversion of kind to string elsewhere. + switch (fn.builtin_kind) { + case BuiltinFunctionKind::None: + break; + case BuiltinFunctionKind::IntAdd: + out_ << " = \"int.add\""; + break; + } + if (!fn.body_block_ids.empty()) { out_ << ' '; OpenBrace(); diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index 3064c60687bd..5482adff1465 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -10,6 +10,15 @@ namespace Carbon::SemIR { +// A builtin function. +// TODO: Move out to another file. +enum class BuiltinFunctionKind : std::uint8_t { + // Not a builtin function. + None, + // "int.add", integer addition. + IntAdd, +}; + // A function. struct Function : public Printable { auto Print(llvm::raw_ostream& out) const -> void { @@ -59,6 +68,11 @@ struct Function : public Printable { // Whether the declaration is extern. bool is_extern; + // The following members are set at the end of a builtin function definition. + + // If this is a builtin function, the corresponding builtin kind. + BuiltinFunctionKind builtin_kind = BuiltinFunctionKind::None; + // The following members are set at the `{` of the function definition. // The definition, if the function has been defined or is currently being