Skip to content

Proposal: remove std.builtin.Type.undefined; the expression undefined requires a result type #22710

@mlugg

Description

@mlugg

RLS has reached a point where it works really nicely as the primary mechanism of type inference; so much so that we have removed anonymous struct types without significant breakage in the wild. The idea here was that anonymous struct type coercions were a legacy feature from before we used result types for type inference.

Zig has two more constructs which obviously fit this bill: null and undefined.

null is unfortunately necessary (at least with how == works today) for x == null to work: since == doesn't forward a result type to either operand, the RHS of == needs to be able to stand without a result type, so null as a standalone expression needs to work. (This could change by changing how == works a little, but that's out of scope for this proposal.)

However, undefined does not match this description. Comparing any value to undefined always results in @as(bool, undefined) (unless the value's type is zero-bit, in which case the comparison is redundant), so it is not a useful construct. In fact, there is no context in which undefined is useful without a result location:

  • a + undefined either invokes Illegal Behavior or returns undefined
  • likewise for all binary operators like +, /, >>, etc
  • likewise for unary operators like -, ~
  • likewise for math-ey builtins like @divExact, @abs, etc
  • passing it to an anytype parameter can only be used by checking for @TypeOf(x) == @Type(.undefined), which is not useful
  • initializing an untyped const with undefined just aliases undefined, which is not useful

Given this, I am proposing the following change to the language:

  • The expression undefined always requires a known result type. It is a compile error if undefined does not have a known result type.
    • So, @TypeOf(undefined) is a compile error.
  • The .undefined variant is removed from std.builtin.Type.

This simplifies the language by removing a type tag, which benefits the language spec (one less type whose behavior must be defined!) and generic Zig code (exhaustively switching on @typeInfo is simpler). It may also make undefined a little easier to understand for beginners, since they'll never have to face the awkward concept of @Type(.undefined); they'll get a useful compile error as soon as they try to misuse the undefined identifier.

Note that under this proposal, undefined retains its status as a builtin identifier which is not a syntactic keyword.

EDIT

I have thought of a realistic use case this will break:

const x = if (something) my_u32 else undefined;

I think code like this ought to have a type annotation for clarity anyway, so if anything, I think this is good, but it is a way in which this proposal is more breaking than I thought.

Note also that #22182 would also break this snippet if accepted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingImplementing this issue could cause existing code to no longer compile or have different behavior.proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions