Search code examples
zig

zig `@ptrToInt()` "error: unable to evaluate constant expression"


I'm attempting to call iotcl to get a terminal size like so:

const std = @import("std");

fn ioctl_TIOCGWINSZ(fd: std.os.fd_t, ws: *std.os.linux.winsize) !void {
    while (true) {
        switch (std.os.errno(std.os.linux.ioctl(fd, std.os.linux.T.IOCGWINSZ, @ptrToInt(ws)))) {
            .SUCCESS => return,
            .INTR => continue,
            .INVAL => unreachable, // invalid request or argument
            .BADF => unreachable,  // fd is not a file descriptor
            .FAULT => unreachable, // invalid argument
            .NOTTY => return std.os.TermiosGetError.NotATerminal, //  fd is not a tty
            else => |err| return std.os.unexpectedErrno(err),
        }
    }
}

pub fn getTerminalSize(handle: std.os.fd_t) !type {

    var size : std.os.linux.winsize = undefined;
    try ioctl_TIOCGWINSZ(handle, &size);

    return .{ size.ws_col, size.ws_row };
}

with the call to getTerminalSize being:

    var size = try tz.getTerminalSize(std.os.STDOUT_FILENO);

However, this produces a compiler error like so:

./libs/terz/terminal.zig:40:79: error: unable to evaluate constant expression
        switch (std.os.errno(std.os.linux.ioctl(fd, std.os.linux.T.IOCGWINSZ, @ptrToInt(ws)))) {
                                                                              ^
./libs/terz/terminal.zig:55:25: note: called from here
    try ioctl_TIOCGWINSZ(handle, &size);
                        ^
./src/main.zig:15:42: note: called from here
        var size = try tz.getTerminalSize(std.os.STDOUT_FILENO);
                                         ^
./src/main.zig:5:29: note: called from here
pub fn main() anyerror!void {
                            ^
./libs/terz/terminal.zig:40:48: note: referenced here
        switch (std.os.errno(std.os.linux.ioctl(fd, std.os.linux.T.IOCGWINSZ, @ptrToInt(ws)))) {
                                               ^
./libs/terz/terminal.zig:55:25: note: referenced here
    try ioctl_TIOCGWINSZ(handle, &size);
                        ^
./src/main.zig:15:42: note: referenced here
        var size = try tz.getTerminalSize(std.os.STDOUT_FILENO);
                                         ^

In std/os/linux.zig, ioctl is defined as:

pub fn ioctl(fd: fd_t, request: u32, arg: usize) usize {
    return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg);
}

And in std.os.zig there's a function (that presumably works) similar to what I'm trying to do:

pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
    while (true) {
        switch (errno(system.ioctl(fd, SIOCGIFINDEX, @ptrToInt(ifr)))) {
            .SUCCESS => return,
            .INVAL => unreachable, // Bad parameters.
            .NOTTY => unreachable,
            .NXIO => unreachable,
            .BADF => unreachable, // Always a race condition.
            .FAULT => unreachable, // Bad pointer parameter.
            .INTR => continue,
            .IO => return error.FileSystem,
            .NODEV => return error.InterfaceNotFound,
            else => |err| return unexpectedErrno(err),
        }
    }
}

Is this a compiler bug, or am I doing something wrong?

Thanks!


Solution

  • So it looks like there's a couple of things wrong with my getTerminalSize(), and the error message is pointing at the wrong culprit here.

    getTerminalSize() shouldn't be returning type. To return an anonymous struct, the syntax would be:

    pub fn getTerminalSize(handle: std.os.fd_t) !struct{ width: u16, height: u16 } {
    
        var size : std.os.linux.winsize = undefined;
        try ioctl_TIOCGWINSZ(handle, &size);
    
        return .{ .width = size.ws_col, .height = size.ws_row };
    }
    

    However, zig doesn't currently support returning anonymous structs unioned with an error type.

    So for the moment, the solution is to name the struct:

    pub const TerminalSize = struct {
        width: u16,
        height: u16,
    };
    
    pub fn getTerminalSize(handle: std.os.fd_t) !TerminalSize {
    
        var size: std.os.linux.winsize = undefined;
        try ioctl_TIOCGWINSZ(handle, &size);
    
        return TerminalSize{ .width = size.ws_col, .height = size.ws_row };
    }