From f0e940ddfd204581ad4cf046f5d2fdf35b20a61e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 21 Mar 2024 13:46:34 -0700 Subject: [PATCH] Initial support for builtin functions. (#3803) For now, a builtin function is defined by specifying a string literal initializer in a function declaration: ```carbon fn MyBuiltin(a: i32) -> i32 = "builtin.name"; ``` End-to-end support is included for a sample `"int.add"` builtin performing integer addition, covering constant evaluation and code generation. The implementation here needs substantial refactoring before we'll be ready to start adding more builtins. That refactoring work will be coming next. This change is aiming to checkpoint some incremental progress. --- common/error.h | 4 +- toolchain/check/eval.cpp | 101 +++++- toolchain/check/handle_function.cpp | 49 +++ toolchain/check/import_ref.cpp | 3 +- toolchain/check/node_stack.h | 2 + .../check/testdata/builtins/int_add.carbon | 301 ++++++++++++++++++ .../testdata/class/fail_addr_self.carbon | 6 +- .../testdata/class/fail_incomplete.carbon | 4 +- .../class/fail_memaccess_category.carbon | 4 +- .../check/testdata/class/fail_method.carbon | 6 +- .../check/testdata/class/fail_self.carbon | 2 +- .../testdata/function/builtin/call.carbon | 43 +++ .../builtin/call_from_operator.carbon | 172 ++++++++++ .../function/builtin/definition.carbon | 25 ++ .../function/builtin/fail_redefined.carbon | 99 ++++++ .../function/builtin/fail_unknown.carbon | 22 ++ .../testdata/function/builtin/import.carbon | 91 ++++++ .../testdata/function/builtin/method.carbon | 97 ++++++ .../function/call/fail_param_count.carbon | 12 +- .../function/call/fail_param_type.carbon | 2 +- ...fail_todo_define_default_fn_inline.carbon} | 40 ++- ..._todo_define_default_fn_out_of_line.carbon | 89 ++++++ .../interface/todo_define_not_default.carbon | 76 +++++ .../testdata/operators/overloaded/eq.carbon | 6 +- .../overloaded/fail_assign_non_ref.carbon | 4 +- .../overloaded/fail_no_impl_for_arg.carbon | 4 +- toolchain/diagnostics/diagnostic_kind.def | 3 + toolchain/lower/file_context.cpp | 6 + toolchain/lower/file_context.h | 3 +- toolchain/lower/handle.cpp | 72 ++++- toolchain/lower/testdata/builtins/int.carbon | 16 + .../builtins/method_vs_nonmethod.carbon | 24 ++ .../builtins/overloaded_operator.carbon | 41 +++ toolchain/parse/extract.cpp | 41 ++- toolchain/parse/handle_function.cpp | 24 ++ toolchain/parse/node_ids.h | 11 +- toolchain/parse/node_kind.def | 38 ++- .../function/definition/builtin.carbon | 37 +++ .../function/definition/fail_builtin.carbon | 50 +++ toolchain/parse/tree.h | 6 +- toolchain/parse/typed_nodes.h | 14 + toolchain/sem_ir/formatter.cpp | 10 + toolchain/sem_ir/function.h | 14 + 43 files changed, 1588 insertions(+), 86 deletions(-) create mode 100644 toolchain/check/testdata/builtins/int_add.carbon create mode 100644 toolchain/check/testdata/function/builtin/call.carbon create mode 100644 toolchain/check/testdata/function/builtin/call_from_operator.carbon create mode 100644 toolchain/check/testdata/function/builtin/definition.carbon create mode 100644 toolchain/check/testdata/function/builtin/fail_redefined.carbon create mode 100644 toolchain/check/testdata/function/builtin/fail_unknown.carbon create mode 100644 toolchain/check/testdata/function/builtin/import.carbon create mode 100644 toolchain/check/testdata/function/builtin/method.carbon rename toolchain/check/testdata/interface/{fail_todo_define_out_of_line.carbon => fail_todo_define_default_fn_inline.carbon} (52%) create mode 100644 toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon create mode 100644 toolchain/check/testdata/interface/todo_define_not_default.carbon create mode 100644 toolchain/lower/testdata/builtins/int.carbon create mode 100644 toolchain/lower/testdata/builtins/method_vs_nonmethod.carbon create mode 100644 toolchain/lower/testdata/builtins/overloaded_operator.carbon create mode 100644 toolchain/parse/testdata/function/definition/builtin.carbon create mode 100644 toolchain/parse/testdata/function/definition/fail_builtin.carbon 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