From f2a16d874233cf90b27e3af6d403a628bc571e9f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 7 May 2025 15:27:29 -0700 Subject: [PATCH] Don't crash if a builtin fn is declared with positional parameters. (#5444) Crash discovered by fuzzer. --- toolchain/check/handle_function.cpp | 5 ++ .../function/builtin/positional.carbon | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 toolchain/check/testdata/function/builtin/positional.carbon diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index c0fbff8f3791..4707d16ae8a0 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -681,6 +681,11 @@ static auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, SemIR::BuiltinFunctionKind builtin_kind) -> bool { + if (!function.call_params_id.has_value()) { + // For now, we have no builtins that support positional parameters. + return false; + } + // Find the list of call parameters other than the implicit return slot. auto call_params = context.inst_blocks().Get(function.call_params_id); if (function.return_slot_pattern_id.has_value()) { diff --git a/toolchain/check/testdata/function/builtin/positional.carbon b/toolchain/check/testdata/function/builtin/positional.carbon new file mode 100644 index 000000000000..399625d35912 --- /dev/null +++ b/toolchain/check/testdata/function/builtin/positional.carbon @@ -0,0 +1,78 @@ +// 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 +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/positional.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/positional.carbon + +// --- fail_positional.carbon + +// CHECK:STDERR: fail_positional.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] +// CHECK:STDERR: fn Add -> i32 = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_positional.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] +// CHECK:STDERR: fn Add -> i32 = "int.sadd"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn Add -> i32 = "int.sadd"; + +// CHECK:STDERR: fail_positional.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] +// CHECK:STDERR: fn Mul = "int.smul"; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_positional.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul" [InvalidBuiltinSignature] +// CHECK:STDERR: fn Mul = "int.smul"; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +fn Mul = "int.smul"; + +// CHECK:STDOUT: --- fail_positional.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] +// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] +// CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] +// CHECK:STDOUT: %Mul.type: type = fn_type @Mul [concrete] +// CHECK:STDOUT: %Mul: %Mul.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Int = %Core.Int +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Add = %Add.decl +// CHECK:STDOUT: .Mul = %Mul.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Add.decl: %Add.type = fn_decl @Add [concrete = constants.%Add] { +// CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] +// CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Mul.decl: %Mul.type = fn_decl @Mul [concrete = constants.%Mul] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Add() -> %i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Mul; +// CHECK:STDOUT: