Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

How do we indicate that functions should be callable during compilation? #748

Open
zygoloid opened this issue Aug 13, 2021 · 9 comments
Open
Labels
leads question A question for the leads team long term Issues expected to take over 90 days to resolve.

Comments

@zygoloid
Copy link
Contributor

In #498, we had a question of whether you can call unannotated functions in a type context:

fn g() -> Type { return Int; }
fn f(x: g()) -> Int { return x; }

The current resolution of #498 suggests that functions would need an explicit annotation in order to be able to be called in this context, perhaps analogous to C 's constexpr. This seems to be easily justified by our code evolution goal.

However, given that we want to support forms of compile-time programming similar to the above, we will need some syntax to indicate the intent to allow uses of g() in constant evaluation, along with everything that implies -- in particular, imposing constraints on future evolution of the function. So what syntax should we use? Some options:

  • A prefix keyword (maybe constexpr or perhaps simply const)
  • A ! somewhere in the function declaration (fn! or g!)
  • An attribute (this would require that we define an attribute syntax, of course)

There are probably many others.

@zygoloid
Copy link
Contributor Author

There's also a semantic question: should a function that is annotated this way be evaluatable only during constant evaluation (like C 's consteval) or usable both during constant evaluation and at runtime (like C 's constexpr)? Both options seem to have significant merit.

Given how central constant evaluation is to Carbon's semantics, I think we can easily justify a punctuation syntax here. Perhaps something like a ! for constexpr semantics and a !! for consteval semantics? (Either after fn or after the function name would seem OK to me.)

@chandlerc
Copy link
Contributor

Do we need both semantics?

I understand the historical reasons why C needed them, but I'm wondering if there is a way to avoid that in Carbon.

@zygoloid
Copy link
Contributor Author

I think we need:

  • a way to write functions that can be shared by compile-time execution and runtime execution (for example, if I want to use the same type during compile-time evaluation and at runtime, I don't want to implement the type twice)
  • a way to write functions that do things that cannot be done at runtime (for example, metaprogramming activities might involve reflection and code generation that we can't support at runtime)

Assuming both situations require a marker, I think we can get away with using the same marker for both, and reject programs that (transitively) use compile-time-only constructs from runtime functions. I think then the question would be: do we want an explicit, declarative statement of intent in code to indicate whether a function is usable at runtime, in the same way we want an explicit, declarative statement of intent in code to indicate whether a function is usable at compile time?

I think the answer might reasonably be no. The only way we wouldn't be able to work this out for ourselves is if a function is intended for compile-time use only but the implementation happens to not use any compile-time-only functionality yet, which seems like a rare situation (quite unlike the situation of a runtime-only function that just happens to not use any runtime-only functionality yet, which is common). We could provide a mechanism to address that narrow issue (such as a compile-time-only operation that happens to be a no-op) if we think it's worthwhile, and then just have a single "usable at compile time" marker that allows runtime use only if the function doesn't transitively depend on anything that's compile-time-only.

@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Nov 12, 2021
@jonmeow jonmeow added leads question A question for the leads team long term Issues expected to take over 90 days to resolve. and removed inactive Issues and PRs which have been inactive for at least 90 days. labels Aug 24, 2022
@chandlerc
Copy link
Contributor

While not urgent, reading this it seems like we're close to a decision here?

Specifically, if we don't try to model both semantics (and allow a future proposal to do a no-op compile-time-only operation if it comes up), then we only need one syntax. @zygoloid had suggested ! as reasonable, on the fn or after the function name.

Do folks have specific reasons to prefer one position or another?

Do folks have specific concerns with using ! in this context?

@zygoloid
Copy link
Contributor Author

I have a mild preference for fn! F(...) rather than fn F!(...). I think the ! is easier to see in that position, and reads as being part of the introducer rather than part of the introduced name / parameters, which I think is logical.

@josh11b
Copy link
Contributor

josh11b commented Sep 20, 2023

I think there are two things we could say here:

  • "constexpr" or "comptime_allowed": Allow this function to be called either during compilation or runtime.
  • "consteval" or "comptime_only": This function may only be called during compilation.
    In both cases, this is in contrast to unannotated functions that may only be called at runtime.

In the first "constexpr" case, a function with the annotation would be during-compilation-only if all its parameters are compile-time-only (declared with :!), as a result of our "we select the comptime version of a function based on whether the arguments are comptime values" decision.

In the second "consteval" case, parameters that aren't marked compile-time-only (declared with :!) wouldn't be usable in type expressions, or wouldn't be allowed at all.

It seems to me that it is going to be common to want an easy way to say "this function should be available at both runtime and compile-time because it performs a pure computation and will be needed in both contexts," and so I think we need a way to say "constexpr."

There is also a use case for "this function computes a type or something else like an initializer that we want to be sure is comptime_only." I'm open to that being "constexpr all parameters are :!" or a separate annotation.

@danakj
Copy link

danakj commented Sep 22, 2023

I would like to bring up a big issue with the consteval model in C 20. I attempted to properly describe it here: chromium/subspace#266 (comment)

The tl;dr is that type_traits run at compile time but they were designed to make decisions about runtime. "Does this function exist" at compile time is really trying to ask "Will this function exist at runtime, such that I can compile a call to it here" and consteval basically breaks type_trait usage because it's not available at runtime.

Rust side steps this whole thing because
a) const things are always avail at runtime too, there's no consteval.
b) compile-time-only stuff is modelled through macros instead which do not interact with things like traits/types.

I think these footguns should be known before copying them from C at least.

@josh11b
Copy link
Contributor

josh11b commented Sep 22, 2023

We are likely going to have compile-time-only values, like types, and compile-time-only functions (for interop with C consteval). In fact, I think we are going to lean more on compile-time functions than macros where we can. So we will definitely need to be careful to make the APIs that query about functions support maintaining that information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
leads question A question for the leads team long term Issues expected to take over 90 days to resolve.
Projects
None yet
Development

No branches or pull requests

5 participants