ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.85k stars 2.55k forks source link

Ability to perform reflection on function parameters #21453

Open VisenDev opened 1 month ago

VisenDev commented 1 month ago

It is possible to reflect on the types of functions but not their names using @typeInfo(function).Fn.params. It would be useful if we had some way of accessing the names of these parameters as well, such as @paramInfo(function)

The specific use case for parameter name reflection is generating luadoc comments from zig functions. See https://github.com/natecraddock/ziglua/issues/94

The ability to perform compile time reflection on parameter names would probably also be useful for any other projects that are generating cross-language bindings or documentation

rohlem commented 1 month ago

The way I understand, this would require functions of differing argument names to result in different types. Currently function types compare equal regardless of argument names (and can be declared without any argument names whatsoever). IMO this would be quite a substantial change, and I personally don't think it's worth it.

On the other hand, function type equality only really matters for function pointer types (which have a runtime representation). Technically we could decay function declaration types when forming a pointer to them to lose argument name info. But this would be a rather specific rule with, to my knowledge, no real parallel anywhere else in status-quo.

The ability to perform compile time reflection on parameter names would probably also be useful for any other projects that are generating cross-language bindings or documentation

Afaik zig's own autodoc feature uses the standard library's parsing pipeline (source code -> tokenizer -> AST). I believe this gives many more features that would be too unwieldy, and not helpful, to encode into @typeInfo / reflection information. I assume whatever use cases you're imagining could similarly benefit from the additional information.

VisenDev commented 1 month ago

The way I understand, this would require functions of differing argument names to result in different types. Currently function types compare equal regardless of argument names (and can be declared without any argument names whatsoever). IMO this would be quite a substantial change, and I personally don't think it's worth it.

I see how that would be the case for implementing this via @typeInfo, hence why I suggested creating a different builtin that doesn't care about types and just returns an array parameters with their names and types -> @paramInfo(function)

rohlem commented 1 month ago

The way I understand, this would require functions of differing argument names to result in different types. Currently function types compare equal regardless of argument names (and can be declared without any argument names whatsoever). IMO this would be quite a substantial change, and I personally don't think it's worth it.

I see how that would be the case for implementing this via @typeInfo, hence why I suggested creating a different builtin that doesn't care about types and just returns an array parameters with their names and types -> @paramInfo(function)

Ah, my bad, it seems I didn't properly read that line. In that case I definitely agree that having additional builtins would be the way to go. Maybe as with @typeInfo we could try a single @srcInfo builtin that returns a union of struct-s to get different information based on the type of entities/declarations inspected. I still wonder when operating on the AST would be more appropriate - but I don't see the downside in exposing as much information as possible to comptime either. It would almost certainly be a simpler, more stable and more comfortable interface to use. (Makes me wonder how far such a feature could even simplify / decouple Zig's own autodoc process.)

VisenDev commented 1 month ago

I still wonder when operating on the AST would be more appropriate

For certain use cases in might be, but this adds a lot of complexity

  • but I don't see the downside in exposing as much information as possible to comptime either. It would almost certainly be a simpler, more stable and more comfortable interface to use.

Yes I agree, needing to reparse your source code in order to do reflection is not an ideal interface. If the compiler has this information already - why not provide in comptime and save the trouble?

tauoverpi commented 1 month ago

Duplicate of https://github.com/ziglang/zig/issues/10706 ?

This would be useful for ECS work to remove the need to have a separate specification for which components are expected by a system's update function and makes using the component pointers to find the base pointer less fragile as one cannot mess up the order as is possible now with a separate specification. The reason for not using a struct is suboptimal codegen. So something like this https://godbolt.org/z/dGGG8hb1v would gain from having such.

rohlem commented 1 month ago

Duplicate of #10706 ?

There is some overlap, but #10706 to me reads as an idea to move information about declarations from current @typeInfo over to another builtin. One of the fields listed is called arg_names, but I don't remember that ever being a part of std.Builtin.Type(Info), so without explicitly being mentioned, it's not clear why it is now there and what the intent is. Under the name @srcInfo (if we were to adopt that) I could imagine exposing a number of other things, like for example doc comments, which were never accessible to comptime before.

The reason for not using a struct is suboptimal codegen.

While I understand the practical concern of making code fast in status-quo, to me having to trade code organization for better optimizations hints at a flaw in the optimization pipeline (and maybe a language flaw if we're missing a way to express the exact guarantees required). I personally would suggest filing a separate issue for that.

tauoverpi commented 1 month ago

@rohlem in the example I want use parameter names to pick components of the system rather than having to specify them twice or pass a struct as there is less redundant information present if so.

Codegen wise I'm not sure what could be proposed to make the struct passing side better other than having having a way to express noalias on struct fields as this is what currently blocks the optimizer as far as I can tell (https://godbolt.org/z/Pvc11xT5E) but as I have far too little knowledge on the optimizer I don't feel I'm fit to write a proposal related to it.