Skip to content

zig updates nov-09-2016 #212

Closed
Closed

Description

@andrewrk

Notes from a 5 hour long phone call with @thejoshwolfe.

I'll close this issue when I finish implementing and/or moving all summary items to appropriate new/existing github issues.

Note that source code below is all brainstorming and may or may not reflect actual syntax and ideas.

Summary:
* named return types solves pointer-to-self.
* no more error union. instead %-> and you must catch errors with %%.
* no tuples are in the future, instead we have multiple return types - possibly named.
* %if% (const value, const value2 = div(3, 2)) {
  } else |err| switch (err) {
      DivByZero => {},
  }
* ?if? (a = b) {}
* all expressions and return types have a number of expressions.
* assembly syntax returns 0 or more expressions
* explicit void cannot be ignored.
* variable declarations are statements not expressions.
* byval copyable parameters and return values. @setIsCopyable(this, true)
* (...name: []const Type) var args. can have Type=var for heterogeneous. (accept https://github.com/andrewrk/zig/issues/77#issuecomment-236307968 "alternatively")


Problems to solve:


 * assigning one field to the address of another in a struct initialization member


struct Foo {
    one: &i32,
    two: i32,


    fn init() -> Foo {
        Foo{
            .two = 1234,
            .one = &this.two,
        }
    }
    fn init() -> result: Foo {
        result = Foo{
            .two = 1234,
            .one = &this.two,
        };
    }
    fn init() -> result: %Foo {
        if (something) return error.BadError;
        result = Foo{
            .two = 1234,
            .one = &this.two,
        };
    }
    fn init() %-> result: Foo {
        if (something) return error.BadError;
        result = Foo{
            .two = 1234,
            .one = &this.two,
        };
    }
}


// example of %return usage being handy
https://github.com/andrewrk/zig/blob/master/std/elf.zig#L186


// broken because one points to invalid memory
var foo = Foo.init();

error DivByZero;
error DivByZero;


/// this comment applies to bar
error DivByZero;
fn bar() {}


/// doc comment applies to div
error DivByZero; // no problemo


fn div(numerator: i32, denominator: i32)
    %-> (quotient: i32, remainder: i32)
{
    if (denominator == 0) return error.DivByZero;
    quotient = numerator / denominator;
    remainder = numerator % denominator;
}


fn main() %-> void {
    if (const value, const value2 %%= div(3, 2)) {
    } else |err| {
        switch (err) {
            DivByZero => {},
        }
    }


    ?if? (const value = maybe_thing) {
    }
    %if% (const value, const value2 = div(3, 2)) {
    } else |err| switch (err) {
        DivByZero => {},
    }


    
    foo.one, foo.two = %return% div(3,2);
    var x2, var y2 = %return% div(3,2);
    var x2, var y2 = div(3,2) %% |err| {
        return err == error.ThatOneError;
    };
    var foo = Foo.init();
    var foo = Foo{...};
    var foo = Foo.init() %% Foo{...};
    var foo = Foo.init() %% {
        foo = Foo{...};
    };
    switch (some_enum_value) {
        One => |blah| {


        },
    }
    var a = getTime() %% |err| {
        printf("error: %s", @errorName(err));
    };
    var a = getTime() %% {-1};
    var a = getTime() %% {
        printf("WARNING: can't get time\n");
        -1
    };


    const x3, const y3 = 💩return div(3, 2);


    const x: i32;
    const y: i32;
irrelevant_stuff();
    if (some_condition) {
        x = 1234;
        y = 5678;
    } else {
        x = 9101;
        y = 1314;
    }


    const x: i32, const y: i32 = if (some_condition) {
        1234, 5678
    } else {
        9101, 1314
    };


    if (some_condition) {
        1234
    } else {
        9101
    };


    _ = 1234;


    _ = aFunction(); // error calling pure function for side effects


    foo.one, foo.two, _ = if (some_condition) {
        1234, 5678, 1
    } else {
        const a, const b = %return div(3,2);
        1, a, b
    };


    b, a = a, b;
    b, a = noop(a, b);


    b, a = {
        f();
        a, b
    };


    b, a = f()(a, b), f()(a, b);
    b = f()(a, b);
    a = f()(a, b);
}


fn noop(a: &const i32, b: &const i32) -> (c: i32, d: i32) {
    c = *a;
    d = *b;
}


fn aFunction() {
   if (a) return sideEffects(1);
   sideEffects(2);
}


fn sideEffects(x: i32) { @breakpoint(); }


 1. people should be able to avoid `undefined`
 2. initializing static data should be same as initializing runtime data






 * asm expressions should be able to return multiple values


  // potentially problematic because only at entry point to this first asm expression do we have guarantee about what registers hold
want this to be atomic


    argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> usize));
    argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8));


    argc, argv = asm(
        \\mov (%%rsp), %[argc]
        \\lea 0x8(%%rsp), %[argv]
        : [argc, argv] "=r" (-> argc: usize, argv: &&u8)
    );


http://llvm.org/docs/LangRef.html#inline-assembler-expressions
http://llvm.org/docs/LangRef.html#extractvalue-instruction




test cases:


matrix265x265.transform(t).transform(t2).scale(-1);


// will it blend?
const result = vector.add(1, 2).add(-1, -3);


fn bad() -> &i32 {
    var x: i32 = undefined;
    return &x; // can the compiler catch this?
}




struct Vec2(T: type) {
    const Self = this;
    @setIsCopyable(Self, true);


    x: T,
    y: T,


    pub fn add(self: &const Self, other_x: T, other_y: T) -> Self {
        Self{
            .x = self.x + other_x,
            .y = self.y + other_y,
        }
    }
    pub fn add(self: &const Self, other_x: T, other_y: T) -> (result: Self) {
        result.x = self.x + other_x;
        result.y = self.y + other_y;
    }
}


type CDW = []u8;
type StdOutPipe = i32;
type IsRedirect = void;
const isRedirect = IsRedirect{};


struct ActualProcess {
    cwd: []u8,
    stdout_pipe: i32,
    isRedirect: bool,
}


pub fn createProcess(...args) {
    var ActualProcess: Process = undefined;
    inline for (args) |arg| {
        inline switch (@typeOf(arg)) {
            CDW => {},
        }
    }
}


myDsl(nameOfThing, anotherOne);


createProcess(CWD("/"), StdOutPipe(1), isRedirect);




fn anotherFn(a: i32, b: i32) {}


pub fn printf(os: &OutStream, inline format: []const u8,
    ...args: []const var) -> %usize
{
    anotherFn(args[0], args[1]) //assuming args[0] and args[1] is type i32
    inline for (args) |arg| {
        if (arg %= @constEval(arg)) {
            // compile time
            new_fmt ++= asdf;
        } else {
            // runtime
        }
    }
}


pub fn print_error(error_code: u32, inline fmt: []const u8, ...args: []var) {
    const error_name = error_names[error_code];
    // this assumes the ++ operator works on compile-time constant-length arrays
    // with variable values at each index.
    const new_args = []var{error_name, error_code} ++ args;
    io.stderr.printf("ERROR: %s(%d): " ++ fmt ++ "\n", ...new_args);
}
// alternatively:
pub fn print_error(error_code: u32, inline fmt: []const u8, ...args: []var) {
    const error_name = error_names[error_code];
    // this assumes a relaxed requirement on passing `...` arguments.
    io.stderr.printf("ERROR: %s(%d): " ++ fmt ++ "\n", error_name, error_code, ...new_args);
}




// If we want the List class to expose ensure_capacity() to callers,
// we can also offer an unchecked add method that skips the capacity check:
pub fn add_unchecked(list: &Self, ...items: []const T) {
    for (items) |item, i| {
        list._buf[list._size + i] = item;
    }
    list._size += items.len;
}
// and then the common add method can reuse the above by forwarding the args:
pub fn add(list: &Self, ...items: []const T) {
    list.ensure_capacity(list._size + items.len);
    list.add_unchecked(...items);
}






fn foo() {}
fn boo() -> void {
    // where's your return?
    return void{};
}


// stuff that you can do in status quo zig that makes no sense
fn foo() {
    var x: i32 = 0;
    return x = 1;
}


fn put(k: K, v: V) -> V {
}


struct Set(inline K: type) {
    map: Map(K, void),
    fn add(s: &Self, k: K) {
        _ = map.put(k, void{});
    }
}


foo(); // good
boo(); // ERROR: ignoring the return type
_ = boo(); // good


fn whatever() % {}
fn whatever() %-> i32 {}


💩return💩
💩if💩


+%
-%
*%
<<%


<<%=


if (x 💩=


fn div(numerator: i32, denominator: i32)
    💩-> (quotient: i32, remainder: i32)
{
}


var a, var b = div(3, 2) 💩💩 






fn draw_particle(t: &Tetris, p: Particle) {
    const model = mat4x4_identity
        .translate_by_vec(p.pos)
        .rotate(p.angle, p.axis)
        .scale(p.scale_w, p.scale_h, 0.0);




Activity

  1. added
    enhancementSolving this issue will likely involve adding new logic or components to the codebase.
    on Nov 9, 2016
  2. added this to the 0.1.0 milestone on Nov 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSolving this issue will likely involve adding new logic or components to the codebase.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions