#60make it so `var` can be known at comptime if not yet mutated

  • avatar~clovercommented · 2 years ago
    Author
    Owner

    Make it so that var assignments to constant values are semantically visible despite being var, can still be observed at comptime. this allows it to be semantically inlined.

    var x: int = 2;
    var s: struct { item: int } = .{ item: 2 }; 
    
    let print = @js("console").log;
    print(s.item);
    

    should lower to this Air:

    Air for <fn() -> void, decl ''>
    X #0 = block {
      S #1 = js_global_ref console
      S #2 = field(#1, log)
      X #3 = call(#2,  [
        <float, 2>
      ])
    }
    

    Examples of clobbers

    var x: int = 2;
    
    if @js("Math").random() > 0.5 {
      x = 3; // analysis of this assignment clobbers the `x` .memory location
    }
    
    var s: struct { item: int } = .{ item: 2 }; 
    
    let print = @js("console").log;
    print(s.item); // no optimization
    
    // this assignment un-clobbers the memory location
    // it is now known at comptime
    x = 4;
    print(x); // inlined 4
    

    I add the optimization tag to this issue, but it will be of sema and it will be a semantic change to how var and assignments work. This means future code with comptime functions can take advantage of this behavior.

    fn doSomethingAtComptime(comptime x: int) { ... }
    
    var x = 3;
    if build_config.should_x_be_four {
      x = 4;
    }
    doSomethingAtComptime(x);
    

    In this snippet, we will expect no errors unless build_config.should_x_be_four is not known at compile time. we will get the error on x saying it is not known at compile time. Previously, x is not comptime because it was using var, but now we can point directly at x = 4 being a clobber in a runtime branch. If we want to be very specific in the error, we could trace the clobber back to the runtime branch and show that in the error message.

    I think this should be reasonably easy to implement, and it will open the door to more semantic optimizations.


    Milestone: 0.3.0.

  • avatar~clover changed the status to Todo · 2 years ago
  • avatar~clovercommented · 2 years ago
    Author
    Owner

    Just got structs to visualize what this currently looks like

    Air for <fn() -> void, decl ''>
    X #0 = block {
      S #1 = memory(<int32, 2>, type: int32)
      S #2 = struct_init(struct { item: int32 }, {
        item: #1
      }
      S #3 = memory(#2, type: struct { item: int32 })
      S #4 = js_global_ref console
      S #5 = field_opaque(#4, log)
      S #6 = field_struct(#3, item)
      X #7 = call(#5,  [
        #6
      ])
    }
    
  • avatar~clovercommented · 2 years ago
    Author
    Owner

    deferring this because to make it work, it will have a lot of edge cases. i didnt originally consider

    var x = 1;
    // x known
    if rt_branch {
      // x known
      x = 3;
      // x known
    }
    // x unknown
    if rt_branch_2 {
      // x unknown
      x = 1;
      // x known
    }