╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Value.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
234 .u64 => |x| @intCast(x), 235 }, 236 else => unreachable, 237 }, 238 }; 239 } 240 241 pub fn toBool(val: Value) bool { 242 return switch (val.toIntern()) { 243 .bool_true => true, 244 .bool_false => false, 245 else => unreachable, 246 }; 247 } 248 249 /// Write a Value's contents to `buffer`. 250 /// 251 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past 252 /// the end of the value in memory. 253 pub fn writeToMemory(val: Value, zcu: *const Zcu, buffer: []u8) error{ 254 ReinterpretDeclRef, 255 IllDefinedMemoryLayout, 256 OutOfMemory, 257 }!void { 258 const target = zcu.getTarget(); 259 const endian = target.cpu.arch.endian(); 260 const ip = &zcu.intern_pool; 261 const ty = val.typeOf(zcu); 262 if (val.isUndef(zcu)) { 263 const size: usize = @intCast(ty.abiSize(zcu)); 264 @memset(buffer[0..size], 0xAA); 265 return; 266 } 267 tag: switch (ty.zigTypeTag(zcu)) { 268 .type => return error.IllDefinedMemoryLayout, 269 .comptime_float => return error.IllDefinedMemoryLayout, 270 .comptime_int => return error.IllDefinedMemoryLayout, 271 .undefined => return error.IllDefinedMemoryLayout, 272 .null => return error.IllDefinedMemoryLayout, 273 .error_union => return error.IllDefinedMemoryLayout, 274 .enum_literal => return error.IllDefinedMemoryLayout, 275 .@"fn" => return error.IllDefinedMemoryLayout, 276 .spirv => return error.IllDefinedMemoryLayout, 277 .@"opaque" => unreachable, 278 .frame => unreachable, 279 .@"anyframe" => unreachable, 280 .noreturn => unreachable, 281 .void => {}, 282 .bool => { 283 buffer[0] = @intFromBool(val.toBool()); 284 }, 285 .pointer => { 286 if (ty.isSlice(zcu)) return error.IllDefinedMemoryLayout; 287 if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef; 288 continue :tag .int; 289 }, 290 .int, .@"enum", .error_set => { 291 var bigint_buffer: BigIntSpace = undefined; 292 const bigint = val.toBigInt(&bigint_buffer, zcu); 293 bigint.writeTwosComplement(buffer[0..@intCast(ty.abiSize(zcu))], endian); 294 }, 295 .float => { 296 const float_bits = ty.floatBits(target); 297 switch (float_bits) { 298 16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(val.toFloat(f16, zcu)), endian), 299 32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(val.toFloat(f32, zcu)), endian), 300 64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(val.toFloat(f64, zcu)), endian), 301 80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(val.toFloat(f80, zcu)), endian), 302 128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(val.toFloat(f128, zcu)), endian), 303 else => unreachable, 304 } 305 const float_bytes = @divExact(float_bits, 8); 306 const total_bytes: usize = @intCast(ty.abiSize(zcu)); 307 @memset(buffer[float_bytes..total_bytes], 0); // padding 308 }, 309 .array => { 310 const aggregate = ip.indexToKey(val.toIntern()).aggregate; 311 const len = ty.arrayLen(zcu); 312 const elem_ty = ty.childType(zcu); 313 const elem_size: usize = @intCast(elem_ty.abiSize(zcu)); 314 var elem_i: usize = 0; 315 var buf_off: usize = 0; 316 while (elem_i < len) : (elem_i += 1) { 317 switch (aggregate.storage) { 318 .bytes => |bytes| buffer[buf_off] = bytes.at(elem_i, ip), 319 .elems => |elems| try Value.fromInterned(elems[elem_i]).writeToMemory(zcu, buffer[buf_off..]), 320 .repeated_elem => |elem| try Value.fromInterned(elem).writeToMemory(zcu, buffer[buf_off..]), 321 } 322 buf_off += elem_size; 323 } 324 if (ty.sentinel(zcu)) |sentinel_val| { 325 try sentinel_val.writeToMemory(zcu, buffer[buf_off..]); 326 } 327 }, 328 .vector => return error.IllDefinedMemoryLayout, 329 .@"struct" => { 330 const struct_type = zcu.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout; 331 switch (struct_type.layout) { 332 .auto => return error.IllDefinedMemoryLayout, 333 .@"extern" => { 334 var last_off: usize = 0; 335 for (struct_type.field_types.get(ip), 0..) |field_ty_ip, field_index| { 336 const off: usize = @intCast(ty.structFieldOffset(field_index, zcu)); 337 @memset(buffer[last_off..off], 0xAA); 338 const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) { 339 .bytes => |bytes| { 340 buffer[off] = bytes.at(field_index, ip); 341 continue; 342 }, 343 .elems => |elems| elems[field_index], 344 .repeated_elem => |elem| elem, 345 }); 346 try writeToMemory(field_val, zcu, buffer[off..]); 347 last_off = @intCast(off + Type.fromInterned(field_ty_ip).abiSize(zcu)); 348 } 349 const struct_size: usize = @intCast(ty.abiSize(zcu)); 350 @memset(buffer[last_off..struct_size], 0xAA); 351 }, 352 .@"packed" => { 353 const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val; 354 return Value.fromInterned(int_index).writeToMemory(zcu, buffer); 355 }, 356 } 357 }, 358 .@"union" => switch (ty.containerLayout(zcu)) { 359 .auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already 360 .@"extern" => { 361 const payload_val = val.unionPayload(zcu); 362 const payload_size: usize = @intCast(payload_val.typeOf(zcu).abiSize(zcu)); 363 const union_size: usize = @intCast(ty.abiSize(zcu)); 364 @memset(buffer[payload_size..union_size], 0xAA); 365 return writeToMemory(payload_val, zcu, buffer); 366 }, 367 .@"packed" => { 368 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val); 369 return writeToMemory(int_val, zcu, buffer); 370 }, 371 }, 372 .optional => { 373 if (!ty.isPtrLikeOptional(zcu)) return error.IllDefinedMemoryLayout; 374 const opt_val = val.optionalValue(zcu); 375 if (opt_val) |some| { 376 return some.writeToMemory(zcu, buffer); 377 } else { 378 const byte_count = Type.usize.abiSize(zcu); 379 @memset(buffer[0..@intCast(byte_count)], 0); // null pointer 380 } 381 }, 382 } 383 } 384 385 /// Write a Value's contents to `buffer`. 386 /// 387 /// Both the start and the end of the provided buffer must be tight, since 388 /// big-endian packed memory layouts start at the end of the buffer. 389 /// 390 /// Supports arrays and vectors, for which the value is written in logical bit 391 /// order, i.e. with the first element at bit offset 0. 392 pub fn writeToPackedMemory( 393 val: Value, 394 zcu: *const Zcu, 395 buffer: []u8, 396 bit_offset: usize, 397 ) void { 398 const ip = &zcu.intern_pool; 399 const target = zcu.getTarget(); 400 const endian = target.cpu.arch.endian(); 401 const ty = val.typeOf(zcu); 402 if (val.isUndef(zcu)) { 403 const bit_size: usize = @intCast(ty.bitSize(zcu)); 404 if (bit_size != 0) { 405 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian); 406 } 407 return; 408 } 409 switch (ty.zigTypeTag(zcu)) { 410 .void => {}, 411 .bool => { 412 const byte_index = switch (endian) { 413 .little => bit_offset / 8, 414 .big => buffer.len - bit_offset / 8 - 1, 415 }; 416 if (val.toBool()) { 417 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8))); 418 } else { 419 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8))); 420 } 421 }, 422 .@"enum" => { 423 const int_val = val.backingInt(zcu); 424 int_val.writeToPackedMemory(zcu, buffer, bit_offset); 425 }, 426 .int => { 427 const bits = ty.intInfo(zcu).bits; 428 if (bits == 0 or buffer.len == 0) return; 429 switch (ip.indexToKey(val.toIntern()).int.storage) { 430 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian), 431 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian), 432 } 433 }, 434 .float => switch (ty.floatBits(target)) { 435 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(val.toFloat(f16, zcu)), endian), 436 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(val.toFloat(f32, zcu)), endian), 437 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(val.toFloat(f64, zcu)), endian), 438 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(val.toFloat(f80, zcu)), endian), 439 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(val.toFloat(f128, zcu)), endian), 440 else => unreachable, 441 }, 442 .@"struct", .@"union" => { 443 assert(ty.containerLayout(zcu) == .@"packed"); 444 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val); 445 int_val.writeToPackedMemory(zcu, buffer, bit_offset); 446 }, 447 .array, .vector => { 448 const elem_bits: usize = @intCast(ty.childType(zcu).bitSize(zcu)); 449 const len: usize = @intCast(ty.arrayLen(zcu)); 450 var elem_bit_off: usize = bit_offset; 451 switch (ip.indexToKey(val.toIntern()).aggregate.storage) { 452 .repeated_elem => |elem_val_ip| { 453 const elem_val: Value = .fromInterned(elem_val_ip); 454 for (0..len) |_| { 455 elem_val.writeToPackedMemory(zcu, buffer, elem_bit_off); 456 elem_bit_off += elem_bits; 457 } 458 }, 459 .elems => |elems| for (elems[0..len]) |elem_val_ip| { 460 const elem_val: Value = .fromInterned(elem_val_ip); 461 elem_val.writeToPackedMemory(zcu, buffer, elem_bit_off); 462 elem_bit_off += elem_bits; 463 }, 464 .bytes => |bytes| for (bytes.toSlice(len, ip)) |raw_byte| { 465 std.mem.writeVarPackedInt(buffer, elem_bit_off, elem_bits, raw_byte, endian); 466 elem_bit_off += elem_bits; 467 }, 468 } 469 if (ty.sentinel(zcu)) |sentinel_val| { 470 sentinel_val.writeToPackedMemory(zcu, buffer, elem_bit_off); 471 } 472 }, 473 else => unreachable, 474 } 475 } 476 477 /// Load a Value from the contents of `buffer`, where `ty` is any integer type. 478 /// 479 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past 480 /// the end of the value in memory. 481 pub fn readIntFromMemory( 482 ty: Type, 483 pt: Zcu.PerThread, 484 buffer: []const u8, 485 arena: Allocator, 486 ) Allocator.Error!Value { 487 const zcu = pt.zcu; 488 const endian = zcu.getTarget().cpu.arch.endian(); 489 490 const int = ty.intInfo(zcu); 491 const abi_size: usize = @intCast(ty.abiSize(zcu)); 492 const exact_buf = buffer[0..abi_size]; 493 494 if (abi_size <= 8) { 495 const shift: u6 = @intCast(64 - int.bits); 496 switch (int.signedness) { 497 .unsigned => { 498 const x = std.mem.readVarInt(u64, exact_buf, endian); 499 return pt.intValue(ty, (x << shift) >> shift); 500 }, 501 .signed => { 502 const x = std.mem.readVarInt(i64, exact_buf, endian); 503 return pt.intValue(ty, (x << shift) >> shift); 504 }, 505 } 506 } else { 507 const limb_count = std.math.big.int.calcTwosCompLimbCount(int.bits); 508 const limbs_buffer = try arena.alloc(std.math.big.Limb, limb_count); 509 510 var bigint: BigIntMutable = .init(limbs_buffer, 0); 511 bigint.readTwosComplement(exact_buf, int.bits, endian, int.signedness); 512 return pt.intValue_big(ty, bigint.toConst()); 513 } 514 } 515 516 /// Load a Value from the contents of `buffer`. 517 /// 518 /// Both the start and the end of the provided buffer must be tight, since 519 /// big-endian packed memory layouts start at the end of the buffer. 520 /// 521 /// Supports arrays and vectors, for which the value is read in logical bit 522 /// order, i.e. with the first element at bit offset 0. 523 pub fn readFromPackedMemory( 524 ty: Type, 525 pt: Zcu.PerThread, 526 buffer: []const u8, 527 bit_offset: usize, 528 ) Allocator.Error!Value { 529 const zcu = pt.zcu; 530 const gpa = zcu.comp.gpa; 531 const target = zcu.getTarget(); 532 const endian = target.cpu.arch.endian(); 533 switch (ty.zigTypeTag(zcu)) { 534 .void => return Value.void, 535 .bool => { 536 const byte = switch (endian) { 537 .big => buffer[buffer.len - bit_offset / 8 - 1], 538 .little => buffer[bit_offset / 8], 539 }; 540 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) { 541 return Value.false; 542 } else { 543 return Value.true; 544 } 545 }, 546 .int => { 547 if (buffer.len == 0) return pt.intValue(ty, 0); 548 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0); 549 const int_info = ty.intInfo(zcu); 550 const bits = int_info.bits; 551 552 // Fast path for integers <= u64 553 if (bits <= 64) switch (int_info.signedness) { 554 // Use different backing types for unsigned vs signed to avoid the need to go via 555 // a larger type like `i128`. 556 .unsigned => return pt.intValue(ty, std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned)), 557 .signed => return pt.intValue(ty, std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed)), 558 }; 559 560 // Slow path, we have to construct a big-int 561 const abi_size: usize = @intCast(ty.abiSize(zcu)); 562 const Limb = std.math.big.Limb; 563 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb); 564 const limbs_buffer = try gpa.alloc(Limb, limb_count); 565 defer gpa.free(limbs_buffer); 566 567 var bigint = BigIntMutable.init(limbs_buffer, 0); 568 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness); 569 return pt.intValue_big(ty, bigint.toConst()); 570 }, 571 .@"enum" => { 572 const int_ty = ty.backingIntType(zcu); 573 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset); 574 return pt.getCoerced(int_val, ty); 575 }, 576 .float => return Value.fromInterned(try pt.intern(.{ .float = .{ 577 .ty = ty.toIntern(), 578 .storage = switch (ty.floatBits(target)) { 579 16 => .{ .f16 = @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian)) }, 580 32 => .{ .f32 = @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian)) }, 581 64 => .{ .f64 = @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian)) }, 582 80 => .{ .f80 = @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian)) }, 583 128 => .{ .f128 = @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian)) }, 584 else => unreachable, 585 }, 586 } })), 587 .@"struct", .@"union" => { 588 assert(ty.containerLayout(zcu) == .@"packed"); 589 const int_val: Value = try .readFromPackedMemory(ty.backingIntType(zcu), pt, buffer, bit_offset); 590 return pt.bitpackValue(ty, int_val); 591 }, 592 .array, .vector => { 593 const elem_ty = ty.childType(zcu); 594 const elem_bits: usize = @intCast(elem_ty.bitSize(zcu)); 595 const elems_buf = try gpa.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); 596 defer gpa.free(elems_buf); 597 var elem_bit_off: usize = bit_offset; 598 for (elems_buf) |*elem| { 599 const elem_val = try readFromPackedMemory(elem_ty, pt, buffer, elem_bit_off); 600 elem.* = elem_val.toIntern(); 601 elem_bit_off += elem_bits; 602 } 603 return pt.aggregateValue(ty, elems_buf); 604 }, 605 else => unreachable, 606 } 607 } 608 609 /// Asserts that the value is a float or an integer. 610 pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T { 611 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 612 .int => |int| switch (int.storage) { 613 .big_int => |big_int| big_int.toFloat(T, .nearest_even)[0], 614 inline .u64, .i64 => |x| @floatFromInt(x), 615 }, 616 .float => |float| switch (float.storage) { 617 inline else => |x| @floatCast(x), 618 }, 619 else => unreachable, 620 }; 621 } 622 623 pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 { 624 var bigint_buf: BigIntSpace = undefined; 625 const bigint = val.toBigInt(&bigint_buf, zcu); 626 return bigint.clz(ty.intInfo(zcu).bits); 627 } 628 629 pub fn ctz(val: Value, ty: Type, zcu: *Zcu) u64 { 630 var bigint_buf: BigIntSpace = undefined; 631 const bigint = val.toBigInt(&bigint_buf, zcu); 632 return bigint.ctz(ty.intInfo(zcu).bits); 633 } 634 635 pub fn popCount(val: Value, ty: Type, zcu: *Zcu) u64 { 636 var bigint_buf: BigIntSpace = undefined; 637 const bigint = val.toBigInt(&bigint_buf, zcu); 638 return @intCast(bigint.popCount(ty.intInfo(zcu).bits)); 639 } 640 641 /// Asserts the value is an integer and not undefined. 642 /// Returns the number of bits the value requires to represent stored in twos complement form. 643 pub fn intBitCountTwosComp(self: Value, zcu: *Zcu) usize { 644 var buffer: BigIntSpace = undefined; 645 const big_int = self.toBigInt(&buffer, zcu); 646 return big_int.bitCountTwosComp(); 647 } 648 649 /// Converts an integer or a float to a float. May result in a loss of information. 650 /// Caller can find out by equality checking the result against the operand. 651 pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value { 652 const zcu = pt.zcu; 653 const target = zcu.getTarget(); 654 if (val.isUndef(zcu)) return pt.undefValue(dest_ty); 655 return Value.fromInterned(try pt.intern(.{ .float = .{ 656 .ty = dest_ty.toIntern(), 657 .storage = switch (dest_ty.floatBits(target)) { 658 16 => .{ .f16 = val.toFloat(f16, zcu) }, 659 32 => .{ .f32 = val.toFloat(f32, zcu) }, 660 64 => .{ .f64 = val.toFloat(f64, zcu) }, 661 80 => .{ .f80 = val.toFloat(f80, zcu) }, 662 128 => .{ .f128 = val.toFloat(f128, zcu) }, 663 else => unreachable, 664 }, 665 } })); 666 } 667 668 /// Asserts the value is comparable. Supports comparisons between heterogeneous types. 669 pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *const Zcu) bool { 670 if (lhs.pointerNav(zcu)) |lhs_nav| { 671 if (rhs.pointerNav(zcu)) |rhs_nav| { 672 switch (op) { 673 .eq => return lhs_nav == rhs_nav, 674 .neq => return lhs_nav != rhs_nav, 675 else => {}, 676 } 677 } else { 678 switch (op) { 679 .eq => return false, 680 .neq => return true, 681 else => {}, 682 } 683 } 684 } else if (rhs.pointerNav(zcu)) |_| { 685 switch (op) { 686 .eq => return false, 687 .neq => return true, 688 else => {}, 689 } 690 } 691 if (lhs.isNan(zcu) or rhs.isNan(zcu)) return op == .neq; 692 return order(lhs, rhs, zcu).compare(op); 693 } 694 695 pub fn order(lhs: Value, rhs: Value, zcu: *const Zcu) std.math.Order { 696 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) { 697 const lhs_f128 = lhs.toFloat(f128, zcu); 698 const rhs_f128 = rhs.toFloat(f128, zcu); 699 return std.math.order(lhs_f128, rhs_f128); 700 } 701 var lhs_bigint_space: BigIntSpace = undefined; 702 var rhs_bigint_space: BigIntSpace = undefined; 703 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu); 704 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu); 705 return lhs_bigint.order(rhs_bigint); 706 } 707 708 /// Asserts the values are comparable. Both operands have type `ty`. 709 /// For vectors, returns true if comparison is true for ALL elements. 710 pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, pt: Zcu.PerThread) !bool { 711 const zcu = pt.zcu; 712 if (ty.zigTypeTag(zcu) == .vector) { 713 const scalar_ty = ty.scalarType(zcu); 714 for (0..ty.vectorLen(zcu)) |i| { 715 const lhs_elem = try lhs.elemValue(pt, i); 716 const rhs_elem = try rhs.elemValue(pt, i); 717 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, zcu)) { 718 return false; 719 } 720 } 721 return true; 722 } 723 return compareScalar(lhs, op, rhs, ty, zcu); 724 } 725 726 /// Asserts the values are comparable. Both operands have type `ty`. 727 pub fn compareScalar( 728 lhs: Value, 729 op: std.math.CompareOperator, 730 rhs: Value, 731 ty: Type, 732 zcu: *Zcu, 733 ) bool { 734 return switch (op) { 735 .eq => lhs.eql(rhs, ty, zcu), 736 .neq => !lhs.eql(rhs, ty, zcu), 737 else => compareHetero(lhs, op, rhs, zcu), 738 }; 739 } 740 741 /// Asserts the value is comparable. 742 /// For vectors, returns true if comparison is true for ALL elements. 743 /// Returns `false` if the value or any vector element is undefined. 744 /// 745 /// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)` 746 pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, zcu: *Zcu) bool { 747 return switch (zcu.intern_pool.indexToKey(lhs.toIntern())) { 748 .float => |float| switch (float.storage) { 749 inline else => |x| std.math.compare(x, op, 0), 750 }, 751 .aggregate => |aggregate| switch (aggregate.storage) { 752 .bytes => |bytes| for (bytes.toSlice( 753 lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu), 754 &zcu.intern_pool, 755 )) |byte| { 756 if (!std.math.compare(byte, op, 0)) break false; 757 } else true, 758 .elems => |elems| for (elems) |elem| { 759 if (!Value.fromInterned(elem).compareAllWithZero(op, zcu)) break false; 760 } else true, 761 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZero(op, zcu), 762 }, 763 .undef => false, 764 else => order(lhs, .zero_comptime_int, zcu).compare(op), 765 }; 766 } 767 768 pub fn eql(a: Value, b: Value, ty: Type, zcu: *Zcu) bool { 769 assert(a.typeOf(zcu).toIntern() == ty.toIntern()); 770 assert(b.typeOf(zcu).toIntern() == ty.toIntern()); 771 return a.toIntern() == b.toIntern(); 772 } 773 774 pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool { 775 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 776 .error_union => |error_union| switch (error_union.val) { 777 .err_name => false, 778 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(zcu), 779 }, 780 .ptr => |ptr| switch (ptr.base_addr) { 781 .nav => false, // The value of a Nav can never reference a comptime alloc. 782 .int => false, 783 .comptime_alloc => true, // A comptime alloc is either mutable or references comptime-mutable memory. 784 .comptime_field => true, // Comptime field pointers are comptime-mutable, albeit only to the "correct" value. 785 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(zcu), 786 .uav => |uav| Value.fromInterned(uav.val).canMutateComptimeVarState(zcu), 787 .arr_elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(zcu), 788 }, 789 .slice => |slice| return Value.fromInterned(slice.ptr).canMutateComptimeVarState(zcu), 790 .opt => |opt| switch (opt.val) { 791 .none => false, 792 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(zcu), 793 }, 794 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| { 795 if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) break true; 796 } else false, 797 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(zcu), 798 else => false, 799 }; 800 } 801 802 /// Gets the `Nav` referenced by this pointer. If the pointer does not point 803 /// to a `Nav`, or if it points to some part of one (like a field or element), 804 /// returns null. 805 pub fn pointerNav(val: Value, zcu: *const Zcu) ?InternPool.Nav.Index { 806 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 807 // TODO: these 3 cases are weird; these aren't pointer values! 808 .@"extern" => |e| e.owner_nav, 809 .func => |func| func.owner_nav, 810 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { 811 .nav => |nav| nav, 812 else => null, 813 } else null, 814 else => null, 815 }; 816 } 817 818 pub const slice_ptr_index = 0; 819 pub const slice_len_index = 1; 820 821 pub fn sliceLen(val: Value, zcu: *Zcu) u64 { 822 return Value.fromInterned(zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedInt(zcu); 823 } 824 pub fn slicePtr(val: Value, zcu: *Zcu) Value { 825 return Value.fromInterned(zcu.intern_pool.slicePtr(val.toIntern())); 826 } 827 828 /// Asserts the value is an aggregate, and returns the element value at the given index. 829 pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Value { 830 const zcu = pt.zcu; 831 const ip = &zcu.intern_pool; 832 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 833 .undef => |ty| { 834 return Value.fromInterned(try pt.intern(.{ .undef = Type.fromInterned(ty).childType(zcu).toIntern() })); 835 }, 836 .aggregate => |aggregate| { 837 const len = ip.aggregateTypeLen(aggregate.ty); 838 if (index < len) return Value.fromInterned(switch (aggregate.storage) { 839 .bytes => |bytes| try pt.intern(.{ .int = .{ 840 .ty = .u8_type, 841 .storage = .{ .u64 = bytes.at(index, ip) }, 842 } }), 843 .elems => |elems| elems[index], 844 .repeated_elem => |elem| elem, 845 }); 846 assert(index == len); 847 return Type.fromInterned(aggregate.ty).sentinel(zcu).?; 848 }, 849 else => unreachable, 850 } 851 } 852 853 pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value { 854 const zcu = pt.zcu; 855 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 856 .undef => |ty| Value.fromInterned(try pt.intern(.{ 857 .undef = Type.fromInterned(ty).fieldType(index, zcu).toIntern(), 858 })), 859 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) { 860 .bytes => |bytes| try pt.intern(.{ .int = .{ 861 .ty = .u8_type, 862 .storage = .{ .u64 = bytes.at(index, &zcu.intern_pool) }, 863 } }), 864 .elems => |elems| elems[index], 865 .repeated_elem => |elem| elem, 866 }), 867 .un => |un| { 868 switch (Type.fromInterned(un.ty).containerLayout(zcu)) { 869 .auto, .@"extern" => {}, // TODO assert the tag is correct 870 .@"packed" => unreachable, 871 } 872 return .fromInterned(un.val); 873 }, 874 .bitpack => |bitpack| { 875 const ty: Type = .fromInterned(bitpack.ty); 876 assert(ty.containerLayout(zcu) == .@"packed"); 877 const int_val: Value = .fromInterned(bitpack.backing_int_val); 878 assert(!int_val.isUndef(zcu)); 879 const field_ty = ty.fieldType(index, zcu); 880 const field_bit_offset: u16 = switch (ty.zigTypeTag(zcu)) { 881 .@"union" => 0, 882 .@"struct" => off: { 883 var off: u16 = 0; 884 for (0..index) |preceding_field_index| { 885 off += @intCast(ty.fieldType(preceding_field_index, zcu).bitSize(zcu)); 886 } 887 break :off off; 888 }, 889 else => unreachable, 890 }; 891 // Avoid hitting gpa for accesses to small packed structs 892 var bfa_buf: [128]u8 = undefined; 893 var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, zcu.comp.gpa); 894 const bfa = bfa_state.allocator(); 895 const buf = try bfa.alloc(u8, @intCast((ty.bitSize(zcu) + 7) / 8)); 896 defer bfa.free(buf); 897 @memset(buf, 0); 898 int_val.writeToPackedMemory(zcu, buf, 0); 899 return .readFromPackedMemory(field_ty, pt, buf, field_bit_offset); 900 }, 901 else => unreachable, 902 }; 903 } 904 905 pub fn unionTag(val: Value, zcu: *const Zcu) ?Value { 906 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 907 .undef, .enum_tag => val, 908 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null, 909 else => unreachable, 910 }; 911 } 912 913 pub fn unionPayload(val: Value, zcu: *const Zcu) Value { 914 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 915 .un => |un| Value.fromInterned(un.val), 916 else => unreachable, 917 }; 918 } 919 920 pub fn isUndef(val: Value, zcu: *const Zcu) bool { 921 return zcu.intern_pool.isUndef(val.toIntern()); 922 } 923 924 /// `val` must have a numeric or vector type. 925 /// Returns whether `val` is undefined or contains any undefined elements. 926 /// Returns the index of the first undefined element it encounters 927 /// or `null` if no element is undefined. 928 pub fn anyScalarIsUndef(val: Value, zcu: *const Zcu) bool { 929 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 930 .undef => return true, 931 .int, .float => return false, 932 .aggregate => |agg| { 933 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector); 934 for (agg.storage.values()) |elem_val| { 935 if (Value.fromInterned(elem_val).isUndef(zcu)) return true; 936 } 937 return false; 938 }, 939 else => unreachable, 940 } 941 } 942 943 /// `val` must have a numeric or vector type. 944 /// Returns whether `val` contains any elements equal to zero. 945 /// Asserts that `val` is not `undefined`, nor a vector containing any `undefined` elements. 946 pub fn anyScalarIsZero(val: Value, zcu: *Zcu) bool { 947 assert(!val.anyScalarIsUndef(zcu)); 948 949 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 950 .int, .float => return val.eqlScalarNum(.zero_comptime_int, zcu), 951 .aggregate => |agg| { 952 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector); 953 switch (agg.storage) { 954 .bytes => |str| { 955 const len = Type.fromInterned(agg.ty).vectorLen(zcu); 956 const slice = str.toSlice(len, &zcu.intern_pool); 957 return std.mem.findScalar(u8, slice, 0) != null; 958 }, 959 .elems => |elems| { 960 for (elems) |elem| { 961 if (Value.fromInterned(elem).isUndef(zcu)) return true; 962 } 963 return false; 964 }, 965 .repeated_elem => |elem| return Value.fromInterned(elem).isUndef(zcu), 966 } 967 }, 968 else => unreachable, 969 } 970 } 971 972 /// Asserts the value is not undefined and not unreachable. 973 /// C pointers with an integer value of 0 are also considered null. 974 pub fn isNull(val: Value, zcu: *Zcu) bool { 975 return switch (val.toIntern()) { 976 .undef => unreachable, 977 .unreachable_value => unreachable, 978 .null_value => true, 979 else => return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 980 .undef => unreachable, 981 .ptr => |ptr| switch (ptr.base_addr) { 982 .int => ptr.byte_offset == 0, 983 else => false, 984 }, 985 .opt => |opt| opt.val == .none, 986 else => false, 987 }, 988 }; 989 } 990 991 /// Valid only for error (union) types. Asserts the value is not undefined and not unreachable. 992 pub fn getErrorName(val: Value, zcu: *const Zcu) InternPool.OptionalNullTerminatedString { 993 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 994 .err => |err| err.name.toOptional(), 995 .error_union => |error_union| switch (error_union.val) { 996 .err_name => |err_name| err_name.toOptional(), 997 .payload => .none, 998 }, 999 else => unreachable, 1000 }; 1001 } 1002 1003 pub fn getErrorInt(val: Value, zcu: *Zcu) Zcu.ErrorInt { 1004 return if (getErrorName(val, zcu).unwrap()) |err_name| 1005 zcu.intern_pool.getErrorValueIfExists(err_name).? 1006 else 1007 0; 1008 } 1009 1010 /// Assumes the type is an error union. Returns true if and only if the value is 1011 /// the error union payload, not an error. 1012 pub fn errorUnionIsPayload(val: Value, zcu: *const Zcu) bool { 1013 return zcu.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload; 1014 } 1015 1016 /// Value of the optional, null if optional has no payload. 1017 pub fn optionalValue(val: Value, zcu: *const Zcu) ?Value { 1018 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 1019 .opt => |opt| switch (opt.val) { 1020 .none => null, 1021 else => |payload| Value.fromInterned(payload), 1022 }, 1023 .ptr => val, 1024 else => unreachable, 1025 }; 1026 } 1027 1028 /// Valid for all types. Asserts the value is not undefined. 1029 pub fn isFloat(self: Value, zcu: *const Zcu) bool { 1030 return switch (self.toIntern()) { 1031 .undef => unreachable, 1032 else => switch (zcu.intern_pool.indexToKey(self.toIntern())) { 1033 .undef => unreachable, 1034 .float => true, 1035 else => false, 1036 }, 1037 }; 1038 } 1039 1040 fn calcLimbLenFloat(scalar: anytype) usize { 1041 if (scalar == 0) { 1042 return 1; 1043 } 1044 1045 const w_value = @abs(scalar); 1046 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).int.bits) + 1; 1047 } 1048 1049 pub const OverflowArithmeticResult = struct { 1050 overflow_bit: Value, 1051 wrapped_result: Value, 1052 }; 1053 1054 pub const OverflowArithmeticResultInt = struct { 1055 overflow: bool, 1056 wrapped_result: Value, 1057 }; 1058 1059 /// Supports both floats and ints; handles undefined. 1060 pub fn numberMax(lhs: Value, rhs: Value, zcu: *Zcu) Value { 1061 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef; 1062 if (lhs.isNan(zcu)) return rhs; 1063 if (rhs.isNan(zcu)) return lhs; 1064 if (compareHetero(lhs, .gt, rhs, zcu)) { 1065 return lhs; 1066 } else { 1067 return rhs; 1068 } 1069 } 1070 1071 /// Supports both floats and ints; handles undefined. 1072 pub fn numberMin(lhs: Value, rhs: Value, zcu: *Zcu) Value { 1073 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef; 1074 if (lhs.isNan(zcu)) return rhs; 1075 if (rhs.isNan(zcu)) return lhs; 1076 if (compareHetero(lhs, .lt, rhs, zcu)) { 1077 return lhs; 1078 } else { 1079 return rhs; 1080 } 1081 } 1082 1083 /// Returns true if the value is a floating point type and is NaN. Returns false otherwise. 1084 pub fn isNan(val: Value, zcu: *const Zcu) bool { 1085 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 1086 .float => |float| switch (float.storage) { 1087 inline else => |x| std.math.isNan(x), 1088 }, 1089 else => false, 1090 }; 1091 } 1092 1093 /// Returns true if the value is a floating point type and is infinite. Returns false otherwise. 1094 pub fn isInf(val: Value, zcu: *const Zcu) bool { 1095 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 1096 .float => |float| switch (float.storage) { 1097 inline else => |x| std.math.isInf(x), 1098 }, 1099 else => false, 1100 }; 1101 } 1102 1103 /// Returns true if the value is a floating point type and is negative infinite. Returns false otherwise. 1104 pub fn isNegativeInf(val: Value, zcu: *const Zcu) bool { 1105 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 1106 .float => |float| switch (float.storage) { 1107 inline else => |x| std.math.isNegativeInf(x), 1108 }, 1109 else => false, 1110 }; 1111 } 1112 1113 pub fn sqrt(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1114 if (float_type.zigTypeTag(pt.zcu) == .vector) { 1115 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(pt.zcu)); 1116 const scalar_ty = float_type.scalarType(pt.zcu); 1117 for (result_data, 0..) |*scalar, i| { 1118 const elem_val = try val.elemValue(pt, i); 1119 scalar.* = (try sqrtScalar(elem_val, scalar_ty, pt)).toIntern(); 1120 } 1121 return pt.aggregateValue(float_type, result_data); 1122 } 1123 return sqrtScalar(val, float_type, pt); 1124 } 1125 1126 pub fn sqrtScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1127 const zcu = pt.zcu; 1128 const target = zcu.getTarget(); 1129 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1130 16 => .{ .f16 = @sqrt(val.toFloat(f16, zcu)) }, 1131 32 => .{ .f32 = @sqrt(val.toFloat(f32, zcu)) }, 1132 64 => .{ .f64 = @sqrt(val.toFloat(f64, zcu)) }, 1133 80 => .{ .f80 = @sqrt(val.toFloat(f80, zcu)) }, 1134 128 => .{ .f128 = @sqrt(val.toFloat(f128, zcu)) }, 1135 else => unreachable, 1136 }; 1137 return Value.fromInterned(try pt.intern(.{ .float = .{ 1138 .ty = float_type.toIntern(), 1139 .storage = storage, 1140 } })); 1141 } 1142 1143 pub fn sin(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1144 const zcu = pt.zcu; 1145 if (float_type.zigTypeTag(zcu) == .vector) { 1146 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1147 const scalar_ty = float_type.scalarType(zcu); 1148 for (result_data, 0..) |*scalar, i| { 1149 const elem_val = try val.elemValue(pt, i); 1150 scalar.* = (try sinScalar(elem_val, scalar_ty, pt)).toIntern(); 1151 } 1152 return pt.aggregateValue(float_type, result_data); 1153 } 1154 return sinScalar(val, float_type, pt); 1155 } 1156 1157 pub fn sinScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1158 const zcu = pt.zcu; 1159 const target = zcu.getTarget(); 1160 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1161 16 => .{ .f16 = @sin(val.toFloat(f16, zcu)) }, 1162 32 => .{ .f32 = @sin(val.toFloat(f32, zcu)) }, 1163 64 => .{ .f64 = @sin(val.toFloat(f64, zcu)) }, 1164 80 => .{ .f80 = @sin(val.toFloat(f80, zcu)) }, 1165 128 => .{ .f128 = @sin(val.toFloat(f128, zcu)) }, 1166 else => unreachable, 1167 }; 1168 return Value.fromInterned(try pt.intern(.{ .float = .{ 1169 .ty = float_type.toIntern(), 1170 .storage = storage, 1171 } })); 1172 } 1173 1174 pub fn cos(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1175 const zcu = pt.zcu; 1176 if (float_type.zigTypeTag(zcu) == .vector) { 1177 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1178 const scalar_ty = float_type.scalarType(zcu); 1179 for (result_data, 0..) |*scalar, i| { 1180 const elem_val = try val.elemValue(pt, i); 1181 scalar.* = (try cosScalar(elem_val, scalar_ty, pt)).toIntern(); 1182 } 1183 return pt.aggregateValue(float_type, result_data); 1184 } 1185 return cosScalar(val, float_type, pt); 1186 } 1187 1188 pub fn cosScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1189 const zcu = pt.zcu; 1190 const target = zcu.getTarget(); 1191 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1192 16 => .{ .f16 = @cos(val.toFloat(f16, zcu)) }, 1193 32 => .{ .f32 = @cos(val.toFloat(f32, zcu)) }, 1194 64 => .{ .f64 = @cos(val.toFloat(f64, zcu)) }, 1195 80 => .{ .f80 = @cos(val.toFloat(f80, zcu)) }, 1196 128 => .{ .f128 = @cos(val.toFloat(f128, zcu)) }, 1197 else => unreachable, 1198 }; 1199 return Value.fromInterned(try pt.intern(.{ .float = .{ 1200 .ty = float_type.toIntern(), 1201 .storage = storage, 1202 } })); 1203 } 1204 1205 pub fn tan(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1206 const zcu = pt.zcu; 1207 if (float_type.zigTypeTag(zcu) == .vector) { 1208 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1209 const scalar_ty = float_type.scalarType(zcu); 1210 for (result_data, 0..) |*scalar, i| { 1211 const elem_val = try val.elemValue(pt, i); 1212 scalar.* = (try tanScalar(elem_val, scalar_ty, pt)).toIntern(); 1213 } 1214 return pt.aggregateValue(float_type, result_data); 1215 } 1216 return tanScalar(val, float_type, pt); 1217 } 1218 1219 pub fn tanScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1220 const zcu = pt.zcu; 1221 const target = zcu.getTarget(); 1222 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1223 16 => .{ .f16 = @tan(val.toFloat(f16, zcu)) }, 1224 32 => .{ .f32 = @tan(val.toFloat(f32, zcu)) }, 1225 64 => .{ .f64 = @tan(val.toFloat(f64, zcu)) }, 1226 80 => .{ .f80 = @tan(val.toFloat(f80, zcu)) }, 1227 128 => .{ .f128 = @tan(val.toFloat(f128, zcu)) }, 1228 else => unreachable, 1229 }; 1230 return Value.fromInterned(try pt.intern(.{ .float = .{ 1231 .ty = float_type.toIntern(), 1232 .storage = storage, 1233 } })); 1234 } 1235 1236 pub fn exp(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1237 const zcu = pt.zcu; 1238 if (float_type.zigTypeTag(zcu) == .vector) { 1239 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1240 const scalar_ty = float_type.scalarType(zcu); 1241 for (result_data, 0..) |*scalar, i| { 1242 const elem_val = try val.elemValue(pt, i); 1243 scalar.* = (try expScalar(elem_val, scalar_ty, pt)).toIntern(); 1244 } 1245 return pt.aggregateValue(float_type, result_data); 1246 } 1247 return expScalar(val, float_type, pt); 1248 } 1249 1250 pub fn expScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1251 const zcu = pt.zcu; 1252 const target = zcu.getTarget(); 1253 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1254 16 => .{ .f16 = @exp(val.toFloat(f16, zcu)) }, 1255 32 => .{ .f32 = @exp(val.toFloat(f32, zcu)) }, 1256 64 => .{ .f64 = @exp(val.toFloat(f64, zcu)) }, 1257 80 => .{ .f80 = @exp(val.toFloat(f80, zcu)) }, 1258 128 => .{ .f128 = @exp(val.toFloat(f128, zcu)) }, 1259 else => unreachable, 1260 }; 1261 return Value.fromInterned(try pt.intern(.{ .float = .{ 1262 .ty = float_type.toIntern(), 1263 .storage = storage, 1264 } })); 1265 } 1266 1267 pub fn exp2(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1268 const zcu = pt.zcu; 1269 if (float_type.zigTypeTag(zcu) == .vector) { 1270 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1271 const scalar_ty = float_type.scalarType(zcu); 1272 for (result_data, 0..) |*scalar, i| { 1273 const elem_val = try val.elemValue(pt, i); 1274 scalar.* = (try exp2Scalar(elem_val, scalar_ty, pt)).toIntern(); 1275 } 1276 return pt.aggregateValue(float_type, result_data); 1277 } 1278 return exp2Scalar(val, float_type, pt); 1279 } 1280 1281 pub fn exp2Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1282 const zcu = pt.zcu; 1283 const target = zcu.getTarget(); 1284 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1285 16 => .{ .f16 = @exp2(val.toFloat(f16, zcu)) }, 1286 32 => .{ .f32 = @exp2(val.toFloat(f32, zcu)) }, 1287 64 => .{ .f64 = @exp2(val.toFloat(f64, zcu)) }, 1288 80 => .{ .f80 = @exp2(val.toFloat(f80, zcu)) }, 1289 128 => .{ .f128 = @exp2(val.toFloat(f128, zcu)) }, 1290 else => unreachable, 1291 }; 1292 return Value.fromInterned(try pt.intern(.{ .float = .{ 1293 .ty = float_type.toIntern(), 1294 .storage = storage, 1295 } })); 1296 } 1297 1298 pub fn log(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1299 const zcu = pt.zcu; 1300 if (float_type.zigTypeTag(zcu) == .vector) { 1301 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1302 const scalar_ty = float_type.scalarType(zcu); 1303 for (result_data, 0..) |*scalar, i| { 1304 const elem_val = try val.elemValue(pt, i); 1305 scalar.* = (try logScalar(elem_val, scalar_ty, pt)).toIntern(); 1306 } 1307 return pt.aggregateValue(float_type, result_data); 1308 } 1309 return logScalar(val, float_type, pt); 1310 } 1311 1312 pub fn logScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1313 const zcu = pt.zcu; 1314 const target = zcu.getTarget(); 1315 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1316 16 => .{ .f16 = @log(val.toFloat(f16, zcu)) }, 1317 32 => .{ .f32 = @log(val.toFloat(f32, zcu)) }, 1318 64 => .{ .f64 = @log(val.toFloat(f64, zcu)) }, 1319 80 => .{ .f80 = @log(val.toFloat(f80, zcu)) }, 1320 128 => .{ .f128 = @log(val.toFloat(f128, zcu)) }, 1321 else => unreachable, 1322 }; 1323 return Value.fromInterned(try pt.intern(.{ .float = .{ 1324 .ty = float_type.toIntern(), 1325 .storage = storage, 1326 } })); 1327 } 1328 1329 pub fn log2(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1330 const zcu = pt.zcu; 1331 if (float_type.zigTypeTag(zcu) == .vector) { 1332 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1333 const scalar_ty = float_type.scalarType(zcu); 1334 for (result_data, 0..) |*scalar, i| { 1335 const elem_val = try val.elemValue(pt, i); 1336 scalar.* = (try log2Scalar(elem_val, scalar_ty, pt)).toIntern(); 1337 } 1338 return pt.aggregateValue(float_type, result_data); 1339 } 1340 return log2Scalar(val, float_type, pt); 1341 } 1342 1343 pub fn log2Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1344 const zcu = pt.zcu; 1345 const target = zcu.getTarget(); 1346 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1347 16 => .{ .f16 = @log2(val.toFloat(f16, zcu)) }, 1348 32 => .{ .f32 = @log2(val.toFloat(f32, zcu)) }, 1349 64 => .{ .f64 = @log2(val.toFloat(f64, zcu)) }, 1350 80 => .{ .f80 = @log2(val.toFloat(f80, zcu)) }, 1351 128 => .{ .f128 = @log2(val.toFloat(f128, zcu)) }, 1352 else => unreachable, 1353 }; 1354 return Value.fromInterned(try pt.intern(.{ .float = .{ 1355 .ty = float_type.toIntern(), 1356 .storage = storage, 1357 } })); 1358 } 1359 1360 pub fn log10(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1361 const zcu = pt.zcu; 1362 if (float_type.zigTypeTag(zcu) == .vector) { 1363 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1364 const scalar_ty = float_type.scalarType(zcu); 1365 for (result_data, 0..) |*scalar, i| { 1366 const elem_val = try val.elemValue(pt, i); 1367 scalar.* = (try log10Scalar(elem_val, scalar_ty, pt)).toIntern(); 1368 } 1369 return pt.aggregateValue(float_type, result_data); 1370 } 1371 return log10Scalar(val, float_type, pt); 1372 } 1373 1374 pub fn log10Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1375 const zcu = pt.zcu; 1376 const target = zcu.getTarget(); 1377 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1378 16 => .{ .f16 = @log10(val.toFloat(f16, zcu)) }, 1379 32 => .{ .f32 = @log10(val.toFloat(f32, zcu)) }, 1380 64 => .{ .f64 = @log10(val.toFloat(f64, zcu)) }, 1381 80 => .{ .f80 = @log10(val.toFloat(f80, zcu)) }, 1382 128 => .{ .f128 = @log10(val.toFloat(f128, zcu)) }, 1383 else => unreachable, 1384 }; 1385 return Value.fromInterned(try pt.intern(.{ .float = .{ 1386 .ty = float_type.toIntern(), 1387 .storage = storage, 1388 } })); 1389 } 1390 1391 pub fn abs(val: Value, ty: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1392 const zcu = pt.zcu; 1393 if (ty.zigTypeTag(zcu) == .vector) { 1394 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(zcu)); 1395 const scalar_ty = ty.scalarType(zcu); 1396 for (result_data, 0..) |*scalar, i| { 1397 const elem_val = try val.elemValue(pt, i); 1398 scalar.* = (try absScalar(elem_val, scalar_ty, pt, arena)).toIntern(); 1399 } 1400 return pt.aggregateValue(ty, result_data); 1401 } 1402 return absScalar(val, ty, pt, arena); 1403 } 1404 1405 pub fn absScalar(val: Value, ty: Type, pt: Zcu.PerThread, arena: Allocator) Allocator.Error!Value { 1406 const zcu = pt.zcu; 1407 switch (ty.zigTypeTag(zcu)) { 1408 .int => { 1409 var buffer: Value.BigIntSpace = undefined; 1410 var operand_bigint = try val.toBigInt(&buffer, zcu).toManaged(arena); 1411 operand_bigint.abs(); 1412 1413 return pt.intValue_big(try ty.toUnsigned(pt), operand_bigint.toConst()); 1414 }, 1415 .comptime_int => { 1416 var buffer: Value.BigIntSpace = undefined; 1417 var operand_bigint = try val.toBigInt(&buffer, zcu).toManaged(arena); 1418 operand_bigint.abs(); 1419 1420 return pt.intValue_big(ty, operand_bigint.toConst()); 1421 }, 1422 .comptime_float, .float => { 1423 const target = zcu.getTarget(); 1424 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) { 1425 16 => .{ .f16 = @abs(val.toFloat(f16, zcu)) }, 1426 32 => .{ .f32 = @abs(val.toFloat(f32, zcu)) }, 1427 64 => .{ .f64 = @abs(val.toFloat(f64, zcu)) }, 1428 80 => .{ .f80 = @abs(val.toFloat(f80, zcu)) }, 1429 128 => .{ .f128 = @abs(val.toFloat(f128, zcu)) }, 1430 else => unreachable, 1431 }; 1432 return Value.fromInterned(try pt.intern(.{ .float = .{ 1433 .ty = ty.toIntern(), 1434 .storage = storage, 1435 } })); 1436 }, 1437 else => unreachable, 1438 } 1439 } 1440 1441 pub fn floor(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1442 const zcu = pt.zcu; 1443 if (float_type.zigTypeTag(zcu) == .vector) { 1444 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1445 const scalar_ty = float_type.scalarType(zcu); 1446 for (result_data, 0..) |*scalar, i| { 1447 const elem_val = try val.elemValue(pt, i); 1448 scalar.* = (try floorScalar(elem_val, scalar_ty, pt)).toIntern(); 1449 } 1450 return pt.aggregateValue(float_type, result_data); 1451 } 1452 return floorScalar(val, float_type, pt); 1453 } 1454 1455 pub fn floorScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1456 const zcu = pt.zcu; 1457 const target = zcu.getTarget(); 1458 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1459 16 => .{ .f16 = @floor(val.toFloat(f16, zcu)) }, 1460 32 => .{ .f32 = @floor(val.toFloat(f32, zcu)) }, 1461 64 => .{ .f64 = @floor(val.toFloat(f64, zcu)) }, 1462 80 => .{ .f80 = @floor(val.toFloat(f80, zcu)) }, 1463 128 => .{ .f128 = @floor(val.toFloat(f128, zcu)) }, 1464 else => unreachable, 1465 }; 1466 return Value.fromInterned(try pt.intern(.{ .float = .{ 1467 .ty = float_type.toIntern(), 1468 .storage = storage, 1469 } })); 1470 } 1471 1472 pub fn ceil(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1473 const zcu = pt.zcu; 1474 if (float_type.zigTypeTag(zcu) == .vector) { 1475 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1476 const scalar_ty = float_type.scalarType(zcu); 1477 for (result_data, 0..) |*scalar, i| { 1478 const elem_val = try val.elemValue(pt, i); 1479 scalar.* = (try ceilScalar(elem_val, scalar_ty, pt)).toIntern(); 1480 } 1481 return pt.aggregateValue(float_type, result_data); 1482 } 1483 return ceilScalar(val, float_type, pt); 1484 } 1485 1486 pub fn ceilScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1487 const zcu = pt.zcu; 1488 const target = zcu.getTarget(); 1489 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1490 16 => .{ .f16 = @ceil(val.toFloat(f16, zcu)) }, 1491 32 => .{ .f32 = @ceil(val.toFloat(f32, zcu)) }, 1492 64 => .{ .f64 = @ceil(val.toFloat(f64, zcu)) }, 1493 80 => .{ .f80 = @ceil(val.toFloat(f80, zcu)) }, 1494 128 => .{ .f128 = @ceil(val.toFloat(f128, zcu)) }, 1495 else => unreachable, 1496 }; 1497 return Value.fromInterned(try pt.intern(.{ .float = .{ 1498 .ty = float_type.toIntern(), 1499 .storage = storage, 1500 } })); 1501 } 1502 1503 pub fn round(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1504 const zcu = pt.zcu; 1505 if (float_type.zigTypeTag(zcu) == .vector) { 1506 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1507 const scalar_ty = float_type.scalarType(zcu); 1508 for (result_data, 0..) |*scalar, i| { 1509 const elem_val = try val.elemValue(pt, i); 1510 scalar.* = (try roundScalar(elem_val, scalar_ty, pt)).toIntern(); 1511 } 1512 return pt.aggregateValue(float_type, result_data); 1513 } 1514 return roundScalar(val, float_type, pt); 1515 } 1516 1517 pub fn roundScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1518 const zcu = pt.zcu; 1519 const target = zcu.getTarget(); 1520 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1521 16 => .{ .f16 = @round(val.toFloat(f16, zcu)) }, 1522 32 => .{ .f32 = @round(val.toFloat(f32, zcu)) }, 1523 64 => .{ .f64 = @round(val.toFloat(f64, zcu)) }, 1524 80 => .{ .f80 = @round(val.toFloat(f80, zcu)) }, 1525 128 => .{ .f128 = @round(val.toFloat(f128, zcu)) }, 1526 else => unreachable, 1527 }; 1528 return Value.fromInterned(try pt.intern(.{ .float = .{ 1529 .ty = float_type.toIntern(), 1530 .storage = storage, 1531 } })); 1532 } 1533 1534 pub fn trunc(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value { 1535 const zcu = pt.zcu; 1536 if (float_type.zigTypeTag(zcu) == .vector) { 1537 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1538 const scalar_ty = float_type.scalarType(zcu); 1539 for (result_data, 0..) |*scalar, i| { 1540 const elem_val = try val.elemValue(pt, i); 1541 scalar.* = (try truncScalar(elem_val, scalar_ty, pt)).toIntern(); 1542 } 1543 return pt.aggregateValue(float_type, result_data); 1544 } 1545 return truncScalar(val, float_type, pt); 1546 } 1547 1548 pub fn truncScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value { 1549 const zcu = pt.zcu; 1550 const target = zcu.getTarget(); 1551 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1552 16 => .{ .f16 = @trunc(val.toFloat(f16, zcu)) }, 1553 32 => .{ .f32 = @trunc(val.toFloat(f32, zcu)) }, 1554 64 => .{ .f64 = @trunc(val.toFloat(f64, zcu)) }, 1555 80 => .{ .f80 = @trunc(val.toFloat(f80, zcu)) }, 1556 128 => .{ .f128 = @trunc(val.toFloat(f128, zcu)) }, 1557 else => unreachable, 1558 }; 1559 return Value.fromInterned(try pt.intern(.{ .float = .{ 1560 .ty = float_type.toIntern(), 1561 .storage = storage, 1562 } })); 1563 } 1564 1565 pub fn mulAdd( 1566 float_type: Type, 1567 mulend1: Value, 1568 mulend2: Value, 1569 addend: Value, 1570 arena: Allocator, 1571 pt: Zcu.PerThread, 1572 ) !Value { 1573 const zcu = pt.zcu; 1574 if (float_type.zigTypeTag(zcu) == .vector) { 1575 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu)); 1576 const scalar_ty = float_type.scalarType(zcu); 1577 for (result_data, 0..) |*scalar, i| { 1578 const mulend1_elem = try mulend1.elemValue(pt, i); 1579 const mulend2_elem = try mulend2.elemValue(pt, i); 1580 const addend_elem = try addend.elemValue(pt, i); 1581 scalar.* = (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, pt)).toIntern(); 1582 } 1583 return pt.aggregateValue(float_type, result_data); 1584 } 1585 return mulAddScalar(float_type, mulend1, mulend2, addend, pt); 1586 } 1587 1588 pub fn mulAddScalar( 1589 float_type: Type, 1590 mulend1: Value, 1591 mulend2: Value, 1592 addend: Value, 1593 pt: Zcu.PerThread, 1594 ) Allocator.Error!Value { 1595 const zcu = pt.zcu; 1596 const target = zcu.getTarget(); 1597 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { 1598 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, zcu), mulend2.toFloat(f16, zcu), addend.toFloat(f16, zcu)) }, 1599 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, zcu), mulend2.toFloat(f32, zcu), addend.toFloat(f32, zcu)) }, 1600 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, zcu), mulend2.toFloat(f64, zcu), addend.toFloat(f64, zcu)) }, 1601 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, zcu), mulend2.toFloat(f80, zcu), addend.toFloat(f80, zcu)) }, 1602 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, zcu), mulend2.toFloat(f128, zcu), addend.toFloat(f128, zcu)) }, 1603 else => unreachable, 1604 }; 1605 return Value.fromInterned(try pt.intern(.{ .float = .{ 1606 .ty = float_type.toIntern(), 1607 .storage = storage, 1608 } })); 1609 } 1610 1611 /// If the value is represented in-memory as a series of bytes that all 1612 /// have the same value, return that byte value, otherwise null. 1613 pub fn hasRepeatedByteRepr(val: Value, zcu: *const Zcu) !?u8 { 1614 const ty = val.typeOf(zcu); 1615 const abi_size = std.math.cast(usize, ty.abiSize(zcu)) orelse return null; 1616 assert(abi_size >= 1); 1617 const byte_buffer = try zcu.gpa.alloc(u8, abi_size); 1618 defer zcu.gpa.free(byte_buffer); 1619 1620 writeToMemory(val, zcu, byte_buffer) catch |err| switch (err) { 1621 error.OutOfMemory => |e| return e, 1622 error.ReinterpretDeclRef => return null, 1623 // TODO: The writeToMemory function was originally created for the purpose 1624 // of comptime pointer casting. However, it is now additionally being used 1625 // for checking the actual memory layout that will be generated by machine 1626 // code late in compilation. So, this error handling is too aggressive and 1627 // causes some false negatives, causing less-than-ideal code generation. 1628 error.IllDefinedMemoryLayout => return null, 1629 }; 1630 const first_byte = byte_buffer[0]; 1631 for (byte_buffer[1..]) |byte| { 1632 if (byte != first_byte) return null; 1633 } 1634 return first_byte; 1635 } 1636 1637 pub fn typeOf(val: Value, zcu: *const Zcu) Type { 1638 return Type.fromInterned(zcu.intern_pool.typeOf(val.toIntern())); 1639 } 1640 1641 /// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value. 1642 /// If `val` is not undef, the bounds are both `val`. 1643 /// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type. 1644 /// If `val` is undef and is a `comptime_int`, returns null. 1645 pub fn intValueBounds(val: Value, pt: Zcu.PerThread) !?[2]Value { 1646 if (!val.isUndef(pt.zcu)) return .{ val, val }; 1647 const ty = pt.zcu.intern_pool.typeOf(val.toIntern()); 1648 if (ty == .comptime_int_type) return null; 1649 return .{ 1650 try Type.fromInterned(ty).minInt(pt, Type.fromInterned(ty)), 1651 try Type.fromInterned(ty).maxInt(pt, Type.fromInterned(ty)), 1652 }; 1653 } 1654 1655 pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace; 1656 1657 pub const undef: Value = .{ .ip_index = .undef }; 1658 pub const undef_bool: Value = .{ .ip_index = .undef_bool }; 1659 pub const undef_usize: Value = .{ .ip_index = .undef_usize }; 1660 pub const undef_u1: Value = .{ .ip_index = .undef_u1 }; 1661 pub const zero_comptime_int: Value = .{ .ip_index = .zero }; 1662 pub const zero_usize: Value = .{ .ip_index = .zero_usize }; 1663 pub const zero_u1: Value = .{ .ip_index = .zero_u1 }; 1664 pub const zero_u8: Value = .{ .ip_index = .zero_u8 }; 1665 pub const one_comptime_int: Value = .{ .ip_index = .one }; 1666 pub const one_usize: Value = .{ .ip_index = .one_usize }; 1667 pub const one_u1: Value = .{ .ip_index = .one_u1 }; 1668 pub const one_u8: Value = .{ .ip_index = .one_u8 }; 1669 pub const four_u8: Value = .{ .ip_index = .four_u8 }; 1670 pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one }; 1671 pub const @"void": Value = .{ .ip_index = .void_value }; 1672 pub const @"unreachable": Value = .{ .ip_index = .unreachable_value }; 1673 pub const @"null": Value = .{ .ip_index = .null_value }; 1674 pub const @"true": Value = .{ .ip_index = .bool_true }; 1675 pub const @"false": Value = .{ .ip_index = .bool_false }; 1676 pub const empty_tuple: Value = .{ .ip_index = .empty_tuple }; 1677 1678 pub fn makeBool(x: bool) Value { 1679 return if (x) .true else .false; 1680 } 1681 1682 /// `parent_ptr` must be a single-pointer or C pointer to some optional. 1683 /// 1684 /// Returns a pointer to the payload of the optional. 1685 pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { 1686 const zcu = pt.zcu; 1687 const parent_ptr_ty = parent_ptr.typeOf(zcu); 1688 const opt_ty = parent_ptr_ty.childType(zcu); 1689 const ptr_size = parent_ptr_ty.ptrSize(zcu); 1690 1691 assert(ptr_size == .one or ptr_size == .c); 1692 assert(opt_ty.zigTypeTag(zcu) == .optional); 1693 1694 const result_ty = try pt.ptrType(info: { 1695 var new = parent_ptr_ty.ptrInfo(zcu); 1696 // We can correctly preserve alignment `.none`, since an optional has the same 1697 // natural alignment as its child type. 1698 new.child = opt_ty.childType(zcu).toIntern(); 1699 break :info new; 1700 }); 1701 1702 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); 1703 1704 if (opt_ty.isPtrLikeOptional(zcu)) { 1705 // Just reinterpret the pointer, since the layout is well-defined 1706 return pt.getCoerced(parent_ptr, result_ty); 1707 } 1708 1709 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, opt_ty, pt); 1710 return .fromInterned(try pt.intern(.{ .ptr = .{ 1711 .ty = result_ty.toIntern(), 1712 .base_addr = .{ .opt_payload = base_ptr.toIntern() }, 1713 .byte_offset = 0, 1714 } })); 1715 } 1716 1717 /// `parent_ptr` must be a single-pointer to some error union. 1718 /// Returns a pointer to the payload of the error union. 1719 pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { 1720 const zcu = pt.zcu; 1721 const parent_ptr_ty = parent_ptr.typeOf(zcu); 1722 const eu_ty = parent_ptr_ty.childType(zcu); 1723 1724 assert(parent_ptr_ty.ptrSize(zcu) == .one); 1725 assert(eu_ty.zigTypeTag(zcu) == .error_union); 1726 1727 const result_ty = try pt.ptrType(info: { 1728 var new = parent_ptr_ty.ptrInfo(zcu); 1729 // We can correctly preserve alignment `.none`, since an error union has a 1730 // natural alignment greater than or equal to that of its payload type. 1731 new.child = eu_ty.errorUnionPayload(zcu).toIntern(); 1732 break :info new; 1733 }); 1734 1735 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); 1736 1737 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, eu_ty, pt); 1738 return .fromInterned(try pt.intern(.{ .ptr = .{ 1739 .ty = result_ty.toIntern(), 1740 .base_addr = .{ .eu_payload = base_ptr.toIntern() }, 1741 .byte_offset = 0, 1742 } })); 1743 } 1744 1745 /// `parent_ptr` must be a single-item pointer or C pointer to a struct, union, or slice. 1746 /// 1747 /// Returns a pointer to the aggregate field at the specified index. 1748 /// 1749 /// For slices, uses `slice_ptr_index` and `slice_len_index`. 1750 /// 1751 /// Asserts that the layout of the aggregate type is resolved. 1752 pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value { 1753 const zcu = pt.zcu; 1754 const parent_ptr_ty = parent_ptr.typeOf(zcu); 1755 const aggregate_ty = parent_ptr_ty.childType(zcu); 1756 aggregate_ty.assertHasLayout(zcu); 1757 1758 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); 1759 assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c); 1760 1761 const field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_idx, pt); 1762 1763 switch (aggregate_ty.zigTypeTag(zcu)) { 1764 .pointer => assert(aggregate_ty.isSlice(zcu)), 1765 .@"struct" => switch (aggregate_ty.containerLayout(zcu)) { 1766 .auto => {}, 1767 .@"extern" => return parent_ptr.getOffsetPtr( 1768 aggregate_ty.structFieldOffset(field_idx, zcu), 1769 field_ptr_ty, 1770 pt, 1771 ), 1772 .@"packed" => return pt.getCoerced(parent_ptr, field_ptr_ty), 1773 }, 1774 .@"union" => switch (aggregate_ty.containerLayout(zcu)) { 1775 .auto => {}, 1776 .@"packed", .@"extern" => return pt.getCoerced(parent_ptr, field_ptr_ty), 1777 }, 1778 else => unreachable, 1779 } 1780 1781 // If we get here, we need to use the `.field` comptime pointer representation, because the 1782 // aggregate does not have a well-defined layout. 1783 1784 if (parent_ptr.isUndef(zcu)) return pt.undefValue(field_ptr_ty); 1785 1786 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, aggregate_ty, pt); 1787 return .fromInterned(try pt.intern(.{ .ptr = .{ 1788 .ty = field_ptr_ty.toIntern(), 1789 .base_addr = .{ .field = .{ 1790 .base = base_ptr.toIntern(), 1791 .index = field_idx, 1792 } }, 1793 .byte_offset = 0, 1794 } })); 1795 } 1796 1797 /// `orig_parent_ptr` must be either a single-pointer to an array, a slice, a many-item pointer, or a C pointer. 1798 /// Returns a pointer to the element at the specified index. 1799 /// Asserts that the layout of the pointer element type is resolved. 1800 pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value { 1801 const zcu = pt.zcu; 1802 const parent_ptr = switch (orig_parent_ptr.typeOf(zcu).ptrSize(zcu)) { 1803 .one, .many, .c => orig_parent_ptr, 1804 .slice => orig_parent_ptr.slicePtr(zcu), 1805 }; 1806 1807 const parent_ptr_ty = parent_ptr.typeOf(zcu); 1808 const result_ty = try parent_ptr_ty.elemPtrType(field_idx, pt); 1809 const elem_ty = result_ty.childType(zcu); 1810 elem_ty.assertHasLayout(zcu); 1811 1812 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); 1813 1814 if (!elem_ty.comptimeOnly(zcu)) { 1815 const byte_offset = field_idx * elem_ty.abiSize(zcu); 1816 return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt); 1817 } 1818 1819 // Comptime-only element type. 1820 1821 if (field_idx == 0) { 1822 return pt.getCoerced(parent_ptr, result_ty); 1823 } 1824 1825 const arr_base_ty, const arr_base_len = elem_ty.arrayBase(zcu); 1826 const base_idx = arr_base_len * field_idx; 1827 const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr; 1828 switch (parent_info.base_addr) { 1829 .arr_elem => |arr_elem| { 1830 if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) { 1831 // We already have a pointer to an element of an array of this type. 1832 // Just modify the index. 1833 return .fromInterned(try pt.intern(.{ .ptr = ptr: { 1834 var new = parent_info; 1835 new.base_addr.arr_elem.index += base_idx; 1836 new.ty = result_ty.toIntern(); 1837 break :ptr new; 1838 } })); 1839 } 1840 }, 1841 else => {}, 1842 } 1843 const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt); 1844 return .fromInterned(try pt.intern(.{ .ptr = .{ 1845 .ty = result_ty.toIntern(), 1846 .base_addr = .{ .arr_elem = .{ 1847 .base = base_ptr.toIntern(), 1848 .index = base_idx, 1849 } }, 1850 .byte_offset = 0, 1851 } })); 1852 } 1853 1854 fn canonicalizeBasePtr(base_ptr: Value, want_size: std.lang.Type.Pointer.Size, want_child: Type, pt: Zcu.PerThread) !Value { 1855 const ptr_ty = base_ptr.typeOf(pt.zcu); 1856 const ptr_info = ptr_ty.ptrInfo(pt.zcu); 1857 1858 if (ptr_info.flags.size == want_size and 1859 ptr_info.child == want_child.toIntern() and 1860 !ptr_info.flags.is_const and 1861 !ptr_info.flags.is_volatile and 1862 !ptr_info.flags.is_allowzero and 1863 ptr_info.sentinel == .none and 1864 ptr_info.flags.alignment == .none) 1865 { 1866 // Already canonical! 1867 return base_ptr; 1868 } 1869 1870 const new_ty = try pt.ptrType(.{ 1871 .child = want_child.toIntern(), 1872 .sentinel = .none, 1873 .flags = .{ 1874 .size = want_size, 1875 .alignment = .none, 1876 .is_const = false, 1877 .is_volatile = false, 1878 .is_allowzero = false, 1879 .address_space = ptr_info.flags.address_space, 1880 }, 1881 }); 1882 return pt.getCoerced(base_ptr, new_ty); 1883 } 1884 1885 pub fn getOffsetPtr(ptr_val: Value, byte_off: u64, new_ty: Type, pt: Zcu.PerThread) !Value { 1886 if (ptr_val.isUndef(pt.zcu)) return ptr_val; 1887 var ptr = pt.zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 1888 ptr.ty = new_ty.toIntern(); 1889 ptr.byte_offset += byte_off; 1890 return Value.fromInterned(try pt.intern(.{ .ptr = ptr })); 1891 } 1892 1893 pub const PointerDeriveStep = union(enum) { 1894 int: struct { 1895 addr: u64, 1896 ptr_ty: Type, 1897 }, 1898 nav_ptr: InternPool.Nav.Index, 1899 uav_ptr: InternPool.Key.Ptr.BaseAddr.Uav, 1900 comptime_alloc_ptr: struct { 1901 idx: InternPool.ComptimeAllocIndex, 1902 val: Value, 1903 ptr_ty: Type, 1904 }, 1905 comptime_field_ptr: Value, 1906 eu_payload_ptr: struct { 1907 parent: *PointerDeriveStep, 1908 /// This type will never be cast: it is provided for convenience. 1909 result_ptr_ty: Type, 1910 }, 1911 opt_payload_ptr: struct { 1912 parent: *PointerDeriveStep, 1913 /// This type will never be cast: it is provided for convenience. 1914 result_ptr_ty: Type, 1915 }, 1916 field_ptr: struct { 1917 parent: *PointerDeriveStep, 1918 field_idx: u32, 1919 /// This type will never be cast: it is provided for convenience. 1920 result_ptr_ty: Type, 1921 }, 1922 elem_ptr: struct { 1923 parent: *PointerDeriveStep, 1924 elem_idx: u64, 1925 /// This type will never be cast: it is provided for convenience. 1926 result_ptr_ty: Type, 1927 }, 1928 offset_and_cast: struct { 1929 parent: *PointerDeriveStep, 1930 byte_offset: u64, 1931 new_ptr_ty: Type, 1932 }, 1933 1934 pub fn ptrType(step: PointerDeriveStep, pt: Zcu.PerThread) !Type { 1935 return switch (step) { 1936 .int => |int| int.ptr_ty, 1937 .nav_ptr => |nav| try pt.navPtrType(nav), 1938 .uav_ptr => |uav| Type.fromInterned(uav.orig_ty), 1939 .comptime_alloc_ptr => |info| info.ptr_ty, 1940 .comptime_field_ptr => |val| try pt.singleConstPtrType(val.typeOf(pt.zcu)), 1941 .offset_and_cast => |oac| oac.new_ptr_ty, 1942 inline .eu_payload_ptr, .opt_payload_ptr, .field_ptr, .elem_ptr => |x| x.result_ptr_ty, 1943 }; 1944 } 1945 }; 1946 1947 /// Given a pointer value, get the sequence of steps to derive it, ideally by taking 1948 /// only field and element pointers with no casts. This can be used by codegen backends 1949 /// which prefer field/elem accesses when lowering constant pointer values. 1950 /// It is also used by the Value printing logic for pointers. 1951 pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep { 1952 const zcu = pt.zcu; 1953 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 1954 const base_derive: PointerDeriveStep = switch (ptr.base_addr) { 1955 .int => return .{ .int = .{ 1956 .addr = ptr.byte_offset, 1957 .ptr_ty = Type.fromInterned(ptr.ty), 1958 } }, 1959 .nav => |nav| .{ .nav_ptr = nav }, 1960 .uav => |uav| base: { 1961 // A slight tweak: `orig_ty` here is sometimes not `const`, but it ought to be. 1962 // TODO: fix this in the sites interning anon decls! 1963 const const_ty = try pt.ptrType(info: { 1964 var info = Type.fromInterned(uav.orig_ty).ptrInfo(zcu); 1965 info.flags.is_const = true; 1966 break :info info; 1967 }); 1968 break :base .{ .uav_ptr = .{ 1969 .val = uav.val, 1970 .orig_ty = const_ty.toIntern(), 1971 } }; 1972 }, 1973 .comptime_alloc => |idx| base: { 1974 const sema = opt_sema.?; 1975 const alloc = sema.getComptimeAlloc(idx); 1976 const val = try alloc.val.intern(pt, arena); 1977 const ty = val.typeOf(zcu); 1978 break :base .{ .comptime_alloc_ptr = .{ 1979 .idx = idx, 1980 .val = val, 1981 .ptr_ty = try pt.ptrType(.{ 1982 .child = ty.toIntern(), 1983 .flags = .{ 1984 .alignment = alloc.alignment, 1985 }, 1986 }), 1987 } }; 1988 }, 1989 .comptime_field => |val| .{ .comptime_field_ptr = Value.fromInterned(val) }, 1990 .eu_payload => |eu_ptr| base: { 1991 const base_ptr = Value.fromInterned(eu_ptr); 1992 const base_ptr_ty = base_ptr.typeOf(zcu); 1993 const parent_step = try arena.create(PointerDeriveStep); 1994 parent_step.* = try pointerDerivation(.fromInterned(eu_ptr), arena, pt, opt_sema); 1995 break :base .{ .eu_payload_ptr = .{ 1996 .parent = parent_step, 1997 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).errorUnionPayload(zcu)), 1998 } }; 1999 }, 2000 .opt_payload => |opt_ptr| base: { 2001 const base_ptr = Value.fromInterned(opt_ptr); 2002 const base_ptr_ty = base_ptr.typeOf(zcu); 2003 const parent_step = try arena.create(PointerDeriveStep); 2004 parent_step.* = try pointerDerivation(.fromInterned(opt_ptr), arena, pt, opt_sema); 2005 break :base .{ .opt_payload_ptr = .{ 2006 .parent = parent_step, 2007 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).optionalChild(zcu)), 2008 } }; 2009 }, 2010 .field => |field| base: { 2011 const base_ptr = Value.fromInterned(field.base); 2012 const base_ptr_ty = try pt.ptrType(info: { 2013 var info = base_ptr.typeOf(zcu).ptrInfo(zcu); 2014 info.flags.size = .one; 2015 break :info info; 2016 }); 2017 const parent_step = try arena.create(PointerDeriveStep); 2018 parent_step.* = try pointerDerivation(base_ptr, arena, pt, opt_sema); 2019 break :base .{ .field_ptr = .{ 2020 .parent = parent_step, 2021 .field_idx = @intCast(field.index), 2022 .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field.index), pt), 2023 } }; 2024 }, 2025 .arr_elem => |arr_elem| base: { 2026 const parent_step = try arena.create(PointerDeriveStep); 2027 parent_step.* = try pointerDerivation(.fromInterned(arr_elem.base), arena, pt, opt_sema); 2028 const parent_ptr_info = (try parent_step.ptrType(pt)).ptrInfo(zcu); 2029 const result_ptr_ty = try pt.ptrType(.{ 2030 .child = parent_ptr_info.child, 2031 .flags = flags: { 2032 var flags = parent_ptr_info.flags; 2033 flags.size = .one; 2034 if (flags.alignment != .none) flags.alignment = .minStrict( 2035 flags.alignment, 2036 Type.fromInterned(parent_ptr_info.child).abiAlignment(zcu), 2037 ); 2038 break :flags flags; 2039 }, 2040 }); 2041 break :base .{ .elem_ptr = .{ 2042 .parent = parent_step, 2043 .elem_idx = arr_elem.index, 2044 .result_ptr_ty = result_ptr_ty, 2045 } }; 2046 }, 2047 }; 2048 2049 if (ptr.byte_offset == 0 and ptr.ty == (try base_derive.ptrType(pt)).toIntern()) { 2050 return base_derive; 2051 } 2052 2053 const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu); 2054 const need_child: Type = .fromInterned(ptr_ty_info.child); 2055 if (need_child.comptimeOnly(zcu) or 2056 need_child.zigTypeTag(zcu) == .@"opaque" or 2057 need_child.isSpirvRuntimeArray(zcu)) 2058 { 2059 // No refinement can happen - this pointer is presumably invalid. 2060 // Just offset it. 2061 const parent = try arena.create(PointerDeriveStep); 2062 parent.* = base_derive; 2063 return .{ .offset_and_cast = .{ 2064 .parent = parent, 2065 .byte_offset = ptr.byte_offset, 2066 .new_ptr_ty = Type.fromInterned(ptr.ty), 2067 } }; 2068 } 2069 const need_bytes = need_child.abiSize(zcu); 2070 2071 var cur_derive = base_derive; 2072 var cur_offset = ptr.byte_offset; 2073 2074 // Refine through fields and array elements as much as possible. 2075 2076 if (need_bytes > 0) while (true) { 2077 const cur_ty = (try cur_derive.ptrType(pt)).childType(zcu); 2078 if (cur_ty.toIntern() == need_child.toIntern() and cur_offset == 0) { 2079 break; 2080 } 2081 switch (cur_ty.zigTypeTag(zcu)) { 2082 .noreturn, 2083 .type, 2084 .comptime_int, 2085 .comptime_float, 2086 .null, 2087 .undefined, 2088 .enum_literal, 2089 .@"opaque", 2090 .spirv, 2091 .@"fn", 2092 .error_union, 2093 .int, 2094 .float, 2095 .bool, 2096 .void, 2097 .pointer, 2098 .error_set, 2099 .@"anyframe", 2100 .frame, 2101 .@"enum", 2102 .vector, 2103 .@"union", 2104 => break, 2105 2106 .optional => { 2107 ptr_opt: { 2108 if (!cur_ty.isPtrLikeOptional(zcu)) break :ptr_opt; 2109 if (need_child.zigTypeTag(zcu) != .pointer) break :ptr_opt; 2110 switch (need_child.ptrSize(zcu)) { 2111 .one, .many => {}, 2112 .slice, .c => break :ptr_opt, 2113 } 2114 const parent = try arena.create(PointerDeriveStep); 2115 parent.* = cur_derive; 2116 cur_derive = .{ .opt_payload_ptr = .{ 2117 .parent = parent, 2118 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), cur_ty.optionalChild(zcu)), 2119 } }; 2120 continue; 2121 } 2122 break; 2123 }, 2124 2125 .array => { 2126 const elem_ty = cur_ty.childType(zcu); 2127 const elem_size = elem_ty.abiSize(zcu); 2128 const start_idx = cur_offset / elem_size; 2129 const end_idx = (cur_offset + need_bytes + elem_size - 1) / elem_size; 2130 if (end_idx == start_idx + 1 and ptr_ty_info.flags.size == .one) { 2131 const parent = try arena.create(PointerDeriveStep); 2132 parent.* = cur_derive; 2133 cur_derive = .{ .elem_ptr = .{ 2134 .parent = parent, 2135 .elem_idx = start_idx, 2136 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), elem_ty), 2137 } }; 2138 cur_offset -= start_idx * elem_size; 2139 } else { 2140 // Go into the first element if needed, but don't go any deeper. 2141 if (start_idx > 0) { 2142 const parent = try arena.create(PointerDeriveStep); 2143 parent.* = cur_derive; 2144 cur_derive = .{ .elem_ptr = .{ 2145 .parent = parent, 2146 .elem_idx = start_idx, 2147 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), elem_ty), 2148 } }; 2149 cur_offset -= start_idx * elem_size; 2150 } 2151 break; 2152 } 2153 }, 2154 .@"struct" => switch (cur_ty.containerLayout(zcu)) { 2155 .auto, .@"packed" => break, 2156 .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| { 2157 const field_ty = cur_ty.fieldType(field_idx, zcu); 2158 const start_off = cur_ty.structFieldOffset(field_idx, zcu); 2159 const end_off = start_off + field_ty.abiSize(zcu); 2160 if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { 2161 const base_ptr_ty = try pt.ptrType(info: { 2162 var info = (try cur_derive.ptrType(pt)).ptrInfo(zcu); 2163 info.flags.size = .one; 2164 break :info info; 2165 }); 2166 const parent = try arena.create(PointerDeriveStep); 2167 parent.* = cur_derive; 2168 cur_derive = .{ .field_ptr = .{ 2169 .parent = parent, 2170 .field_idx = @intCast(field_idx), 2171 .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field_idx), pt), 2172 } }; 2173 cur_offset -= start_off; 2174 break; 2175 } 2176 } else break, // pointer spans multiple fields 2177 }, 2178 } 2179 }; 2180 2181 if (cur_offset == 0) compatible: { 2182 const src_ptr_ty_info = (try cur_derive.ptrType(pt)).ptrInfo(zcu); 2183 // We allow silently doing some "coercible" pointer things. 2184 // In particular, we only give up if cv qualifiers are *removed*. 2185 if (src_ptr_ty_info.flags.is_const and !ptr_ty_info.flags.is_const) break :compatible; 2186 if (src_ptr_ty_info.flags.is_volatile and !ptr_ty_info.flags.is_volatile) break :compatible; 2187 if (src_ptr_ty_info.flags.is_allowzero and !ptr_ty_info.flags.is_allowzero) break :compatible; 2188 // Everything else has to match exactly. 2189 if (src_ptr_ty_info.child != ptr_ty_info.child) break :compatible; 2190 if (src_ptr_ty_info.sentinel != ptr_ty_info.sentinel) break :compatible; 2191 if (src_ptr_ty_info.packed_offset != ptr_ty_info.packed_offset) break :compatible; 2192 if (src_ptr_ty_info.flags.size != ptr_ty_info.flags.size) break :compatible; 2193 if (src_ptr_ty_info.flags.alignment != ptr_ty_info.flags.alignment) break :compatible; 2194 if (src_ptr_ty_info.flags.address_space != ptr_ty_info.flags.address_space) break :compatible; 2195 if (src_ptr_ty_info.flags.vector_index != ptr_ty_info.flags.vector_index) break :compatible; 2196 2197 return cur_derive; 2198 } 2199 2200 const parent = try arena.create(PointerDeriveStep); 2201 parent.* = cur_derive; 2202 return .{ .offset_and_cast = .{ 2203 .parent = parent, 2204 .byte_offset = cur_offset, 2205 .new_ptr_ty = Type.fromInterned(ptr.ty), 2206 } }; 2207 } 2208 2209 const InterpretMode = enum { 2210 /// In this mode, types are assumed to match what the compiler was built with in terms of field 2211 /// order, field types, etc. This improves compiler performance. However, it means that certain 2212 /// modifications to `std.lang` will result in compiler crashes. 2213 direct, 2214 /// In this mode, various details of the type are allowed to differ from what the compiler was built 2215 /// with. Fields are matched by name rather than index; added struct fields are ignored, and removed 2216 /// struct fields use their default value if one exists. This is slower than `.direct`, but permits 2217 /// making certain changes to `std.lang` (in particular reordering/adding/removing fields), so it is 2218 /// useful when applying breaking changes. 2219 by_name, 2220 }; 2221 const interpret_mode: InterpretMode = @field(InterpretMode, @tagName(build_options.value_interpret_mode)); 2222 2223 /// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler. 2224 /// This is useful for accessing `std.lang` structures received from comptime logic. 2225 pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T { 2226 const zcu = pt.zcu; 2227 const io = zcu.comp.io; 2228 const ip = &zcu.intern_pool; 2229 const ty = val.typeOf(zcu); 2230 if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch; 2231 if (val.isUndef(zcu)) return error.UndefinedValue; 2232 2233 return switch (@typeInfo(T)) { 2234 .type, 2235 .noreturn, 2236 .comptime_float, 2237 .comptime_int, 2238 .undefined, 2239 .null, 2240 .@"fn", 2241 .@"opaque", 2242 .spirv, 2243 .enum_literal, 2244 => comptime unreachable, // comptime-only or otherwise impossible 2245 2246 .pointer, 2247 .array, 2248 .error_union, 2249 .error_set, 2250 .frame, 2251 .@"anyframe", 2252 .vector, 2253 => comptime unreachable, // unsupported 2254 2255 .void => {}, 2256 2257 .bool => switch (val.toIntern()) { 2258 .bool_false => false, 2259 .bool_true => true, 2260 else => unreachable, 2261 }, 2262 2263 .int => switch (ip.indexToKey(val.toIntern()).int.storage) { 2264 inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch, 2265 .big_int => |big| big.toInt(T) catch return error.TypeMismatch, 2266 }, 2267 2268 .float => val.toFloat(T, zcu), 2269 2270 .optional => |opt| if (val.optionalValue(zcu)) |unwrapped| 2271 try unwrapped.interpret(opt.child, pt) 2272 else 2273 null, 2274 2275 .@"enum" => switch (interpret_mode) { 2276 .direct => { 2277 const int = val.getUnsignedInt(zcu) orelse return error.TypeMismatch; 2278 return std.enums.fromInt(T, int) orelse error.TypeMismatch; 2279 }, 2280 .by_name => { 2281 const field_index = ty.enumTagFieldIndex(val, zcu) orelse return error.TypeMismatch; 2282 const field_name = ty.enumFieldName(field_index, zcu); 2283 return std.meta.stringToEnum(T, field_name.toSlice(ip)) orelse error.TypeMismatch; 2284 }, 2285 }, 2286 2287 .@"union" => |@"union"| { 2288 // No need to handle `interpret_mode`, because the `.@"enum"` handling already deals with it. 2289 const tag_val = val.unionTag(zcu) orelse return error.TypeMismatch; 2290 const tag = try tag_val.interpret(@"union".tag_type.?, pt); 2291 return switch (tag) { 2292 inline else => |tag_comptime| @unionInit( 2293 T, 2294 @tagName(tag_comptime), 2295 try val.unionPayload(zcu).interpret(@FieldType(T, @tagName(tag_comptime)), pt), 2296 ), 2297 }; 2298 }, 2299 2300 .@"struct" => |@"struct"| switch (interpret_mode) { 2301 .direct => { 2302 if (ty.structFieldCount(zcu) != @"struct".field_names.len) return error.TypeMismatch; 2303 var result: T = undefined; 2304 inline for (@"struct".field_names, @"struct".field_types, 0..) |field_name, field_type, field_idx| { 2305 const field_val = try val.fieldValue(pt, field_idx); 2306 @field(result, field_name) = try field_val.interpret(field_type, pt); 2307 } 2308 return result; 2309 }, 2310 .by_name => { 2311 const struct_obj = zcu.typeToStruct(ty) orelse return error.TypeMismatch; 2312 var result: T = undefined; 2313 inline for (@"struct".field_names, @"struct".field_types, @"struct".field_attrs) |field_name, field_type, field_attr| { 2314 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field_name, .no_embedded_nulls); 2315 @field(result, field_name) = if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| f: { 2316 const field_val = try val.fieldValue(pt, field_idx); 2317 break :f try field_val.interpret(field_type, pt); 2318 } else (field_attr.defaultValue(field_type) orelse return error.TypeMismatch); 2319 } 2320 return result; 2321 }, 2322 }, 2323 }; 2324 } 2325 2326 /// Given any `val` and a `Type` corresponding `@TypeOf(val)`, construct a `Value` representing it which can be used 2327 /// within the compilation. This is useful for passing `std.lang` structures in the compiler back to the compilation. 2328 /// This is the inverse of `interpret`. 2329 pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory, TypeMismatch }!Value { 2330 const T = @TypeOf(val); 2331 2332 const zcu = pt.zcu; 2333 const io = zcu.comp.io; 2334 const ip = &zcu.intern_pool; 2335 if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch; 2336 2337 return switch (@typeInfo(T)) { 2338 .type, 2339 .noreturn, 2340 .comptime_float, 2341 .comptime_int, 2342 .undefined, 2343 .null, 2344 .@"fn", 2345 .@"opaque", 2346 .spirv, 2347 .enum_literal, 2348 => comptime unreachable, // comptime-only or otherwise impossible 2349 2350 .pointer, 2351 .array, 2352 .error_union, 2353 .error_set, 2354 .frame, 2355 .@"anyframe", 2356 .vector, 2357 => comptime unreachable, // unsupported 2358 2359 .void => .void, 2360 2361 .bool => if (val) .true else .false, 2362 2363 .int => try pt.intValue(ty, val), 2364 2365 .float => try pt.floatValue(ty, val), 2366 2367 .optional => if (val) |some| 2368 .fromInterned(try pt.intern(.{ .opt = .{ 2369 .ty = ty.toIntern(), 2370 .val = (try uninterpret(some, ty.optionalChild(zcu), pt)).toIntern(), 2371 } })) 2372 else 2373 try pt.nullValue(ty), 2374 2375 .@"enum" => switch (interpret_mode) { 2376 .direct => try pt.enumValue(ty, try uninterpret(@backingInt(val), ty.backingIntType(zcu), pt)), 2377 .by_name => { 2378 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls); 2379 const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch; 2380 return pt.enumValueFieldIndex(ty, field_idx); 2381 }, 2382 }, 2383 2384 .@"union" => |@"union"| { 2385 // No need to handle `interpret_mode`, because the `.@"enum"` handling already deals with it. 2386 const tag: @"union".tag_type.? = val; 2387 const tag_val = try uninterpret(tag, ty.unionTagType(zcu).?, pt); 2388 const field_ty = ty.unionFieldType(tag_val, zcu) orelse return error.TypeMismatch; 2389 return switch (val) { 2390 inline else => |payload| try pt.unionValue( 2391 ty, 2392 tag_val, 2393 try uninterpret(payload, field_ty, pt), 2394 ), 2395 }; 2396 }, 2397 2398 .@"struct" => |@"struct"| switch (interpret_mode) { 2399 .direct => { 2400 if (ty.structFieldCount(zcu) != @"struct".field_names.len) return error.TypeMismatch; 2401 var field_vals: [@"struct".field_names.len]InternPool.Index = undefined; 2402 inline for (&field_vals, @"struct".field_names, 0..) |*field_val, field_name, field_idx| { 2403 const field_ty = ty.fieldType(field_idx, zcu); 2404 field_val.* = (try uninterpret(@field(val, field_name), field_ty, pt)).toIntern(); 2405 } 2406 return pt.aggregateValue(ty, &field_vals); 2407 }, 2408 .by_name => { 2409 const struct_obj = zcu.typeToStruct(ty) orelse return error.TypeMismatch; 2410 const want_fields_len = struct_obj.field_types.len; 2411 const field_vals = try zcu.gpa.alloc(InternPool.Index, want_fields_len); 2412 defer zcu.gpa.free(field_vals); 2413 @memset(field_vals, .none); 2414 inline for (@"struct".field_names) |field_name| { 2415 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field_name, .no_embedded_nulls); 2416 if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| { 2417 const field_ty = ty.fieldType(field_idx, zcu); 2418 field_vals[field_idx] = (try uninterpret(@field(val, field_name), field_ty, pt)).toIntern(); 2419 } 2420 } 2421 for (field_vals, 0..) |*field_val, field_idx| { 2422 if (field_val.* == .none) { 2423 const default_init = struct_obj.field_defaults.get(ip)[field_idx]; 2424 if (default_init == .none) return error.TypeMismatch; 2425 field_val.* = default_init; 2426 } 2427 } 2428 return pt.aggregateValue(ty, field_vals); 2429 }, 2430 }, 2431 }; 2432 } 2433 2434 /// Returns whether `ptr_val_a[0..elem_count]` and `ptr_val_b[0..elem_count]` overlap. 2435 /// `ptr_val_a` and `ptr_val_b` are indexable pointers (not slices) whose element types are in-memory coercible. 2436 pub fn doPointersOverlap(ptr_val_a: Value, ptr_val_b: Value, elem_count: u64, zcu: *const Zcu) bool { 2437 const ip = &zcu.intern_pool; 2438 2439 const a_elem_ty = ptr_val_a.typeOf(zcu).indexableElem(zcu); 2440 const b_elem_ty = ptr_val_b.typeOf(zcu).indexableElem(zcu); 2441 2442 const a_ptr = ip.indexToKey(ptr_val_a.toIntern()).ptr; 2443 const b_ptr = ip.indexToKey(ptr_val_b.toIntern()).ptr; 2444 2445 // If `a_elem_ty` is not comptime-only, then overlapping pointers have identical 2446 // `base_addr`, and we just need to look at the byte offset. If it *is* comptime-only, 2447 // then `base_addr` may be an `arr_elem`, and we'll have to consider the element index. 2448 if (a_elem_ty.comptimeOnly(zcu)) { 2449 assert(a_elem_ty.toIntern() == b_elem_ty.toIntern()); // IMC comptime-only types are equivalent 2450 2451 const a_base_addr: InternPool.Key.Ptr.BaseAddr, const a_idx: u64 = switch (a_ptr.base_addr) { 2452 else => .{ a_ptr.base_addr, 0 }, 2453 .arr_elem => |arr_elem| a: { 2454 const base_ptr = Value.fromInterned(arr_elem.base); 2455 const base_child_ty = base_ptr.typeOf(zcu).childType(zcu); 2456 if (base_child_ty.toIntern() == a_elem_ty.toIntern()) { 2457 // This `arr_elem` is indexing into the element type we want. 2458 const base_ptr_info = ip.indexToKey(base_ptr.toIntern()).ptr; 2459 if (base_ptr_info.byte_offset != 0) { 2460 return false; // this pointer is invalid, just let the access fail 2461 } 2462 break :a .{ base_ptr_info.base_addr, arr_elem.index }; 2463 } 2464 break :a .{ a_ptr.base_addr, 0 }; 2465 }, 2466 }; 2467 const b_base_addr: InternPool.Key.Ptr.BaseAddr, const b_idx: u64 = switch (a_ptr.base_addr) { 2468 else => .{ b_ptr.base_addr, 0 }, 2469 .arr_elem => |arr_elem| b: { 2470 const base_ptr = Value.fromInterned(arr_elem.base); 2471 const base_child_ty = base_ptr.typeOf(zcu).childType(zcu); 2472 if (base_child_ty.toIntern() == b_elem_ty.toIntern()) { 2473 // This `arr_elem` is indexing into the element type we want. 2474 const base_ptr_info = ip.indexToKey(base_ptr.toIntern()).ptr; 2475 if (base_ptr_info.byte_offset != 0) { 2476 return false; // this pointer is invalid, just let the access fail 2477 } 2478 break :b .{ base_ptr_info.base_addr, arr_elem.index }; 2479 } 2480 break :b .{ b_ptr.base_addr, 0 }; 2481 }, 2482 }; 2483 if (!std.meta.eql(a_base_addr, b_base_addr)) return false; 2484 const diff = if (a_idx >= b_idx) a_idx - b_idx else b_idx - a_idx; 2485 return diff < elem_count; 2486 } else { 2487 assert(a_elem_ty.abiSize(zcu) == b_elem_ty.abiSize(zcu)); 2488 2489 if (!std.meta.eql(a_ptr.base_addr, b_ptr.base_addr)) return false; 2490 2491 const bytes_diff = if (a_ptr.byte_offset >= b_ptr.byte_offset) 2492 a_ptr.byte_offset - b_ptr.byte_offset 2493 else 2494 b_ptr.byte_offset - a_ptr.byte_offset; 2495 2496 const need_bytes_diff = elem_count * a_elem_ty.abiSize(zcu); 2497 return bytes_diff < need_bytes_diff; 2498 } 2499 } 2500 2501 /// `lhs` and `rhs` are both scalar numeric values (int or float). 2502 /// Supports comparisons between heterogeneous types. 2503 /// If `lhs` or `rhs` is undef, returns `false`. 2504 pub fn eqlScalarNum(lhs: Value, rhs: Value, zcu: *Zcu) bool { 2505 if (lhs.isUndef(zcu)) return false; 2506 if (rhs.isUndef(zcu)) return false; 2507 2508 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) { 2509 const lhs_f128 = lhs.toFloat(f128, zcu); 2510 const rhs_f128 = rhs.toFloat(f128, zcu); 2511 return lhs_f128 == rhs_f128; 2512 } 2513 2514 if (lhs.getUnsignedInt(zcu)) |lhs_u64| { 2515 if (rhs.getUnsignedInt(zcu)) |rhs_u64| { 2516 return lhs_u64 == rhs_u64; 2517 } 2518 } 2519 2520 var lhs_bigint_space: BigIntSpace = undefined; 2521 var rhs_bigint_space: BigIntSpace = undefined; 2522 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu); 2523 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu); 2524 return lhs_bigint.eql(rhs_bigint); 2525 } 2526 2527 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 2528 /// Vectors are also accepted. Vector results are reduced with AND. 2529 /// 2530 /// If provided, `vector_index` reports the first element that failed the range check. 2531 pub fn intFitsInType( 2532 val: Value, 2533 ty: Type, 2534 vector_index: ?*usize, 2535 zcu: *const Zcu, 2536 ) bool { 2537 if (ty.toIntern() == .comptime_int_type) return true; 2538 const info = ty.intInfo(zcu); 2539 switch (val.toIntern()) { 2540 .zero_usize, .zero_u8 => return true, 2541 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { 2542 .undef => return true, 2543 .@"extern", .func, .ptr => { 2544 const target = zcu.getTarget(); 2545 const ptr_bits = target.ptrBitWidth(); 2546 return switch (info.signedness) { 2547 .signed => info.bits > ptr_bits, 2548 .unsigned => info.bits >= ptr_bits, 2549 }; 2550 }, 2551 .int => |int| { 2552 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 2553 const big_int = int.storage.toBigInt(&buffer); 2554 return big_int.fitsInTwosComp(info.signedness, info.bits); 2555 }, 2556 .aggregate => |aggregate| { 2557 assert(ty.zigTypeTag(zcu) == .vector); 2558 return switch (aggregate.storage) { 2559 .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| { 2560 if (byte == 0) continue; 2561 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 2562 if (info.bits >= actual_needed_bits) continue; 2563 if (vector_index) |vi| vi.* = i; 2564 break false; 2565 } else true, 2566 .elems, .repeated_elem => for (switch (aggregate.storage) { 2567 .bytes => unreachable, 2568 .elems => |elems| elems, 2569 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 2570 }, 0..) |elem, i| { 2571 if (Value.fromInterned(elem).intFitsInType(ty.scalarType(zcu), null, zcu)) continue; 2572 if (vector_index) |vi| vi.* = i; 2573 break false; 2574 } else true, 2575 }; 2576 }, 2577 else => unreachable, 2578 }, 2579 } 2580 } 2581