Skip to content

Tags #1099

@BraedonWooding

Description

@BraedonWooding

This was discussed originally here, and elsewhere :).

Motivation

I've been looking at implementing lambda functions (by first making all functions anonymous) but I feel like I should begin with something a little easier as to help me understand the codebase; I've recently had need for reflection metadata in a codegen which generates getters/setters (amongst other things) for structs in relation to a stack based system for a markup language that translates to a IR/bytecode representation, anyways it would be nice to allow users to disable/hide this generation with a hide tag (as well as customise such as link getters/setters to other functions). Amongst other tags, regardless this would be a nice feature I'm sure many authors would enjoy. I'll be looking towards implementing this, this upcoming weekend but I wanted to gather opinion on syntax :).

Proposal

The original proposal was this;

const Foo = struct {
    x: u32 @("anything") @(bar()),
};

fn bar() i32 {
    return 1234;
}

However this proposes a problem of how one would access the tags, since they aren't purely the same type; in the majority of cases (specifically I can't think of a case where this isn't true) you just want a string tag not a 'boolean' tag since in reality having something like @(true) is meaningless compared to @("Hide") so I propose the following restriction; all tags are just strings and @("X") is replaced with @tag("X") this makes it a little simpler to read and a lot simpler to actually handle.

const Foo = struct {
  x: u32 @tag("A") @tag("B"),
};

Also tags will be able to be applied to almost anything that is globally scoped that is functions (global), structs (note: variables can't have tags) having something like;

fn X() void @tag("MyFunc") {
    ...
}

You would access it like;

// For Struct
for (@typeInfo(Foo).Struct.fields) |field| {
    for (field.Tags) |tag| {
       // Do whatever with tag
    }
}

// For function
for (@typeInfo(X).Fn) |tag| {
    // Do whatever with tag
}

You could also query all objects that have a certain tag like;

comptime const query = @withTag("A"); // returning typeInfo

Now this would look through each typeInfo available returning a static array of all that have it, however we could also maintain a map of each tag to a list of typeinfo making this much much more efficient but increase the size of the running compiler (I feel a valid tradeoff).

Actual Changes

  • Syntax Change: allow @tag(...) after types in global 'objects' (functions, structs), currently only @tag(...) but probably add syntactically support for any attribute (in terms of format @<...>(<...>) but lexically only support tag, this would allow us to expand it later for other attributes if wanted
  • Builtin.zig change (autogen that is); add 'tags' field to StructField, and Fn that is a string slice (u8 const)
  • Add @tag(tagName: []const u8) and @withTag(tagName: []const u8)->[]const @import("builtin").TypeInfo
    • Note: @tag is not grouped under attributes perhaps but rather is separate as it doesn't have a return type

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions