Files
Richard Smith 1b2ae912fc Add basic support for eval fn and musteval fn. (#6694)
Add support for compile-time functions. `eval fn` is analogous to C++
`constexpr`, and is evaluated at compile time when it has compile-time
arguments. `musteval fn` is analogous to C++ `consteval`, and requires
that its arguments be available at compile time and is always evaluated
at compile time. For now we require the modifier to match across
redeclarations of the function. The specific modifier syntax here is a
placeholder and not yet part of an approved design.

Limitations: Only very basic support for evaluation is provided. So far
there's no support for mutable state or `if` expressions, but otherwise
control flow and passing and returning values should work. Carbon
evaluation recursion is modeled by C++ recursion for now, so you can
overflow the toolchain stack easily. Functions that use in-place
initialization will generally not work yet, as they are modeled as
passing a non-compile-time-constant reference to a temporary to the
call.

Add missing categorization of `name_binding_decl` as `NotExpr` to match
other similar declaration instructions like `FunctionDecl`, so that we
can uniformly skip over them when they occur within function bodies.

Assisted-by: Gemini 3 Pro and Flash via Antigravity
2026-02-11 02:08:16 +00:00

173 lines
6.8 KiB
Plaintext

// 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
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/eval_musteval.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/eval_musteval.carbon
// --- basic.carbon
library "[[@TEST_NAME]]";
fn Basic() -> i32 { return 0; }
eval fn Eval() -> i32 { return 0; }
musteval fn MustEval() -> i32 { return 0; }
// --- member.carbon
library "[[@TEST_NAME]]";
class C {
eval fn Eval() {}
musteval fn MustEval() {}
}
// --- private.carbon
library "[[@TEST_NAME]]";
private eval fn Eval() -> i32 { return 0; }
private musteval fn MustEval() -> i32 { return 0; }
// --- fail_conflict.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_conflict.carbon:[[@LINE+7]]:6: error: `musteval` not allowed on declaration with `eval` [ModifierNotAllowedWith]
// CHECK:STDERR: eval musteval fn Mixed();
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_conflict.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious]
// CHECK:STDERR: eval musteval fn Mixed();
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval musteval fn Mixed();
// --- fail_order.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:6: error: `export` must appear before `eval` [ModifierMustAppearBefore]
// CHECK:STDERR: eval export fn ExportEval();
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious]
// CHECK:STDERR: eval export fn ExportEval();
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval export fn ExportEval();
// CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:6: error: `private` must appear before `eval` [ModifierMustAppearBefore]
// CHECK:STDERR: eval private fn PrivateEval();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious]
// CHECK:STDERR: eval private fn PrivateEval();
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval private fn PrivateEval();
class C {
// CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:8: error: `virtual` must appear before `eval` [ModifierMustAppearBefore]
// CHECK:STDERR: eval virtual fn VirtualEval();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:3: note: `eval` previously appeared here [ModifierPrevious]
// CHECK:STDERR: eval virtual fn VirtualEval();
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval virtual fn VirtualEval();
}
// --- fail_not_fn.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_not_fn.carbon:[[@LINE+4]]:1: error: `eval` not allowed on `var` declaration [ModifierNotAllowedOnDeclaration]
// CHECK:STDERR: eval var x: i32;
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval var x: i32;
// CHECK:STDERR: fail_not_fn.carbon:[[@LINE+4]]:1: error: `eval` not allowed on `class` declaration [ModifierNotAllowedOnDeclaration]
// CHECK:STDERR: eval class C {};
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
eval class C {};
// --- redecl.carbon
library "[[@TEST_NAME]]";
eval fn A() -> i32;
eval fn A() -> i32 { return 0; }
musteval fn B() -> i32;
musteval fn B() -> i32 { return 0; }
// --- fail_differ_in_redecl.carbon
library "[[@TEST_NAME]]";
fn NoneThenEval();
fn NoneThenMustEval();
eval fn EvalThenNone();
eval fn EvalThenMustEval();
musteval fn MustEvalThenNone();
musteval fn MustEvalThenEval();
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `eval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: eval fn NoneThenEval() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-10]]:1: note: previously not declared as `eval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: fn NoneThenEval();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
eval fn NoneThenEval() {}
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `musteval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: musteval fn NoneThenMustEval() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-18]]:1: note: previously not declared as `musteval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: fn NoneThenMustEval();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
musteval fn NoneThenMustEval() {}
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is not `eval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: fn EvalThenNone() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-26]]:1: note: previously declared as `eval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: eval fn EvalThenNone();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn EvalThenNone() {}
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `musteval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: musteval fn EvalThenMustEval() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-34]]:1: note: previously declared as `eval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: eval fn EvalThenMustEval();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
musteval fn EvalThenMustEval() {}
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is not `musteval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: fn MustEvalThenNone() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-42]]:1: note: previously declared as `musteval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: musteval fn MustEvalThenNone();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
fn MustEvalThenNone() {}
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `eval` [FunctionRedeclEvaluationModeDiffers]
// CHECK:STDERR: eval fn MustEvalThenEval() {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-50]]:1: note: previously declared as `musteval` [FunctionRedeclEvaluationModePrevious]
// CHECK:STDERR: musteval fn MustEvalThenEval();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
eval fn MustEvalThenEval() {}