Skip to content

Proposal: Improve type inference of if/switch with comptime-known operand #13025

Open
@topolarity

Description

@topolarity

Problem

Comptime emulation will sometimes assign different types to the same syntax as runtime code:

A. if/switch don't perform peer type resolution for comptime arguments, affecting downstream code:

comptime { // works if we comment out `comptime`
    const base: usize = 15;
    var y: union(enum) { offset: usize, unknown: void } = .{ .offset = 12 };

    const maybe_x = switch(y) {
       .offset => |off| off + base,
       .unknown => null,
    };
    if (maybe_x) |x| { // error: expected optional type, found 'usize'
        ...
    }
}

B. In a comptime context, all tuple fields are all inferred to be comptime, making them suddenly immutable:

comptime { // works if we comment out `comptime`
    var x: usize = 1;
    var y = .{ x, x + 1 };
    y = .{ 3, 4 }; // error: value stored in comptime field does not match the default value of the field
}

(A) is particularly problematic, since it means that Zig cannot improve its semantic analysis like Andrews mentions here:

... zig reserves the right to make the if static if it can prove the value is compile-time known (which it should be able to do in this example with more sophisticated semantic analysis).

Unfortunately if we don't fix this problem, improving semantic analysis is a breaking change for users.

So what can be done about it?

Proposed solution

A value is "semantically comptime" if it is:

  1. a literal constant, OR
  2. labeled with comptime at its declaration site, OR
  3. the result of a comptime ... expression, OR
  4. the result of an operator/builtin with semantically comptime inputs, OR
  5. a const whose initialization expression is semantically comptime

In a runtime context, all comptime-known values are also semantically comptime. However in a comptime context, there are values that are comptime-known but not semantically comptime. For example:

const x = comptime b: { // x is both comptime-known and semantically comptime
    var y: usize = 1; // comptime-known, but not semantically comptime
    break :b y;
};

A. Branching in switch/if

When branching on a comptime-known but not semantically comptime value in a switch/if/orelse/catch or a non-inline while/for, Sema must perform "runtime-equivalent analysis" as follows:

  • Semantically comptime values (from before branch entry) are treated as immutable.
  • Peer type resolution is performed the same way it would be at runtime.
  • In dead branches:
    • Comptime-known, but not semantically comptime values are treated as runtime values.
    • No runtime code is generated during this analysis.

B. Tuple fields

Tuple fields should only be inferred to be comptime fields if they are initialized with a semantically comptime value.

This would resolve #12261. The existing tuple semantics also accidentally make the rejected #7539 implementable in userspace, which is resolved by this proposal.

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

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions