Add new `set` keyword

4ef0a4c342dfed4f34598dbacf345e91b72955ee1cafa0270c23db912199eee4
Alexis Sellier committed ago 1 parent 05324b73
lib/std/lang/ast.rad +15 -0
813 813
    span: Span,
814 814
    /// Variant-specific payload for the node.
815 815
    value: NodeValue,
816 816
}
817 817
818 +
/// Check whether a node is a place expression.
819 +
///
820 +
/// Place expressions have persistent storage and can appear on the left side
821 +
/// of `set` assignments.
822 +
export fn isPlaceExpr(node: *Node) -> bool {
823 +
    match node.value {
824 +
        case NodeValue::Ident(_),
825 +
             NodeValue::ScopeAccess(_),
826 +
             NodeValue::FieldAccess(_),
827 +
             NodeValue::Subscript { .. },
828 +
             NodeValue::Deref(_) => return true,
829 +
        else => return false,
830 +
    }
831 +
}
832 +
818 833
/// Allocate a new AST node from the arena with the given span and value.
819 834
export fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
820 835
    let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node));
821 836
    let node = p as *mut Node;
822 837
    let nodeId = arena.nextId;
lib/std/lang/gen/bitset.rad +1 -1
55 55
56 56
    return init(bits);
57 57
}
58 58
59 59
/// Set bit `n` in the bitset.
60 -
export fn set(bs: *mut Bitset, n: u32) {
60 +
export fn put(bs: *mut Bitset, n: u32) {
61 61
    if n >= bs.len {
62 62
        return;
63 63
    }
64 64
    let word = n / 32;
65 65
    let b = n % 32;
lib/std/lang/gen/bitset/tests.rad +37 -37
12 12
    try testing::expect(not super::contains(&bs, 31));
13 13
    try testing::expect(not super::contains(&bs, 32));
14 14
    try testing::expect(not super::contains(&bs, 127));
15 15
16 16
    // Set various bits including word boundaries.
17 -
    super::set(&mut bs, 0);
18 -
    super::set(&mut bs, 31);
19 -
    super::set(&mut bs, 32);
20 -
    super::set(&mut bs, 127);
17 +
    super::put(&mut bs, 0);
18 +
    super::put(&mut bs, 31);
19 +
    super::put(&mut bs, 32);
20 +
    super::put(&mut bs, 127);
21 21
22 22
    try testing::expect(super::contains(&bs, 0));
23 23
    try testing::expect(super::contains(&bs, 31));
24 24
    try testing::expect(super::contains(&bs, 32));
25 25
    try testing::expect(super::contains(&bs, 127));
34 34
/// Test clear operation.
35 35
@test fn testClear() throws (testing::TestError) {
36 36
    let mut bits: [u32; 2] = [0; 2];
37 37
    let mut bs = super::new(&mut bits[..]);
38 38
39 -
    super::set(&mut bs, 0);
40 -
    super::set(&mut bs, 31);
41 -
    super::set(&mut bs, 32);
39 +
    super::put(&mut bs, 0);
40 +
    super::put(&mut bs, 31);
41 +
    super::put(&mut bs, 32);
42 42
43 43
    try testing::expect(super::contains(&bs, 0));
44 44
    try testing::expect(super::contains(&bs, 31));
45 45
    try testing::expect(super::contains(&bs, 32));
46 46
56 56
    let mut bits: [u32; 2] = [0; 2];
57 57
    let mut bs = super::new(&mut bits[..]);
58 58
59 59
    try testing::expect(super::count(&bs) == 0);
60 60
61 -
    super::set(&mut bs, 0);
61 +
    super::put(&mut bs, 0);
62 62
    try testing::expect(super::count(&bs) == 1);
63 63
64 -
    super::set(&mut bs, 31);
65 -
    super::set(&mut bs, 32);
66 -
    super::set(&mut bs, 63);
64 +
    super::put(&mut bs, 31);
65 +
    super::put(&mut bs, 32);
66 +
    super::put(&mut bs, 63);
67 67
    try testing::expect(super::count(&bs) == 4);
68 68
69 69
    super::clear(&mut bs, 31);
70 70
    try testing::expect(super::count(&bs) == 3);
71 71
}
75 75
    let mut bits_a: [u32; 2] = [0; 2];
76 76
    let mut bits_b: [u32; 2] = [0; 2];
77 77
    let mut a = super::new(&mut bits_a[..]);
78 78
    let mut b = super::new(&mut bits_b[..]);
79 79
80 -
    super::set(&mut a, 0);
81 -
    super::set(&mut a, 10);
80 +
    super::put(&mut a, 0);
81 +
    super::put(&mut a, 10);
82 82
83 -
    super::set(&mut b, 10);
84 -
    super::set(&mut b, 20);
83 +
    super::put(&mut b, 10);
84 +
    super::put(&mut b, 20);
85 85
86 86
    super::union_(&mut a, &b);
87 87
88 88
    try testing::expect(super::contains(&a, 0));
89 89
    try testing::expect(super::contains(&a, 10));
96 96
    let mut bits_a: [u32; 2] = [0; 2];
97 97
    let mut bits_b: [u32; 2] = [0; 2];
98 98
    let mut a = super::new(&mut bits_a[..]);
99 99
    let mut b = super::new(&mut bits_b[..]);
100 100
101 -
    super::set(&mut a, 0);
102 -
    super::set(&mut a, 10);
103 -
    super::set(&mut a, 20);
101 +
    super::put(&mut a, 0);
102 +
    super::put(&mut a, 10);
103 +
    super::put(&mut a, 20);
104 104
105 -
    super::set(&mut b, 10);
106 -
    super::set(&mut b, 30);
105 +
    super::put(&mut b, 10);
106 +
    super::put(&mut b, 30);
107 107
108 108
    super::subtract(&mut a, &b);
109 109
110 110
    try testing::expect(super::contains(&a, 0));
111 111
    try testing::expect(not super::contains(&a, 10));
121 121
    let mut b = super::new(&mut bits_b[..]);
122 122
123 123
    // Both empty.
124 124
    try testing::expect(super::eq(&a, &b));
125 125
126 -
    super::set(&mut a, 5);
126 +
    super::put(&mut a, 5);
127 127
    try testing::expect(not super::eq(&a, &b));
128 128
129 -
    super::set(&mut b, 5);
129 +
    super::put(&mut b, 5);
130 130
    try testing::expect(super::eq(&a, &b));
131 131
132 -
    super::set(&mut a, 32);
133 -
    super::set(&mut b, 32);
132 +
    super::put(&mut a, 32);
133 +
    super::put(&mut b, 32);
134 134
    try testing::expect(super::eq(&a, &b));
135 135
136 -
    super::set(&mut b, 33);
136 +
    super::put(&mut b, 33);
137 137
    try testing::expect(not super::eq(&a, &b));
138 138
}
139 139
140 140
/// Test copy operation.
141 141
@test fn testCopy() throws (testing::TestError) {
142 142
    let mut bits_a: [u32; 2] = [0; 2];
143 143
    let mut bits_b: [u32; 2] = [0; 2];
144 144
    let mut a = super::new(&mut bits_a[..]);
145 145
    let mut b = super::new(&mut bits_b[..]);
146 146
147 -
    super::set(&mut a, 0);
148 -
    super::set(&mut a, 31);
149 -
    super::set(&mut a, 63);
147 +
    super::put(&mut a, 0);
148 +
    super::put(&mut a, 31);
149 +
    super::put(&mut a, 63);
150 150
151 151
    super::copy(&mut b, &a);
152 152
153 153
    try testing::expect(super::eq(&a, &b));
154 154
    try testing::expect(super::contains(&b, 0));
159 159
/// Test clearAll operation.
160 160
@test fn testClearAll() throws (testing::TestError) {
161 161
    let mut bits: [u32; 2] = [0; 2];
162 162
    let mut bs = super::new(&mut bits[..]);
163 163
164 -
    super::set(&mut bs, 0);
165 -
    super::set(&mut bs, 31);
166 -
    super::set(&mut bs, 32);
167 -
    super::set(&mut bs, 63);
164 +
    super::put(&mut bs, 0);
165 +
    super::put(&mut bs, 31);
166 +
    super::put(&mut bs, 32);
167 +
    super::put(&mut bs, 63);
168 168
    try testing::expect(super::count(&bs) == 4);
169 169
170 170
    super::clearAll(&mut bs);
171 171
    try testing::expect(super::count(&bs) == 0);
172 172
    try testing::expect(not super::contains(&bs, 0));
176 176
/// Test iteration over set bits.
177 177
@test fn testIter() throws (testing::TestError) {
178 178
    let mut bits: [u32; 2] = [0; 2];
179 179
    let mut bs = super::new(&mut bits[..]);
180 180
181 -
    super::set(&mut bs, 3);
182 -
    super::set(&mut bs, 31);
183 -
    super::set(&mut bs, 32);
184 -
    super::set(&mut bs, 50);
181 +
    super::put(&mut bs, 3);
182 +
    super::put(&mut bs, 31);
183 +
    super::put(&mut bs, 32);
184 +
    super::put(&mut bs, 50);
185 185
186 186
    let mut it = super::iter(&bs);
187 187
    let mut count: u32 = 0;
188 188
    let mut sum: u32 = 0;
189 189
221 221
@test fn testOutOfBounds() throws (testing::TestError) {
222 222
    let mut bits: [u32; 1] = [0; 1];
223 223
    let mut bs = super::new(&mut bits[..]);
224 224
225 225
    // Setting beyond length should be ignored.
226 -
    super::set(&mut bs, 100);
226 +
    super::put(&mut bs, 100);
227 227
    try testing::expect(not super::contains(&bs, 100));
228 228
229 229
    // Clearing beyond length should be safe.
230 230
    super::clear(&mut bs, 100);
231 231
lib/std/lang/gen/regalloc/assign.rad +3 -3
110 110
            for k in 0..pred.n {
111 111
                if bitset::contains(&live.liveIn[b], pred.virtRegs[k]) {
112 112
                    current.virtRegs[current.n] = pred.virtRegs[k];
113 113
                    current.physRegs[current.n] = pred.physRegs[k];
114 114
                    current.n += 1;
115 -
                    bitset::set(&mut usedRegs, *pred.physRegs[k] as u32);
115 +
                    bitset::put(&mut usedRegs, *pred.physRegs[k] as u32);
116 116
                }
117 117
            }
118 118
        }
119 119
120 120
        // Mark all live-in values' registers as used.
126 126
        let mut liveInIter = bitset::iter(&live.liveIn[b]);
127 127
        while let ssaReg = bitset::iterNext(&mut liveInIter) {
128 128
            let reg = il::Reg { n: ssaReg };
129 129
            if not spill::isSpilled(spillInfo, reg) {
130 130
                if let phys = assignments[ssaReg] {
131 -
                    bitset::set(&mut usedRegs, *phys as u32);
131 +
                    bitset::put(&mut usedRegs, *phys as u32);
132 132
                    // Also track in current mapping for block-end state.
133 133
                    if rmapFind(&current, ssaReg) == nil {
134 134
                        rmapSet(&mut current, ssaReg, phys);
135 135
                    }
136 136
                } else {
239 239
/// Find first free register in pool, allocate it, return it.
240 240
fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
241 241
    for i in 0..pool.len {
242 242
        let r = pool[i];
243 243
        if not bitset::contains(usedRegs, *r as u32) {
244 -
            bitset::set(usedRegs, *r as u32);
244 +
            bitset::put(usedRegs, *r as u32);
245 245
            rmapSet(current, ssaReg, r);
246 246
            return r;
247 247
        }
248 248
    }
249 249
    return nil;
lib/std/lang/gen/regalloc/liveness.rad +3 -3
158 158
}
159 159
160 160
/// Compute local defs and uses for a single block.
161 161
fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) {
162 162
    for p in block.params {
163 -
        bitset::set(defs, p.value.n);
163 +
        bitset::put(defs, p.value.n);
164 164
    }
165 165
    for i in 0..block.instrs.len {
166 166
        let instr = block.instrs[i];
167 167
        let mut ctx = DefsUses { defs, uses };
168 168
        il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque);
169 169
170 170
        if let dst = il::instrDst(instr) {
171 -
            bitset::set(defs, dst.n);
171 +
            bitset::put(defs, dst.n);
172 172
        }
173 173
    }
174 174
}
175 175
176 176
/// Callback for [`il::forEachReg`]: adds register to uses if not already defined.
177 177
fn addUseCallback(reg: il::Reg, ctx: *mut opaque) {
178 178
    let c = ctx as *mut DefsUses;
179 179
    if not bitset::contains(c.defs, reg.n) {
180 -
        bitset::set(c.uses, reg.n);
180 +
        bitset::put(c.uses, reg.n);
181 181
    }
182 182
}
183 183
184 184
/// Callback for [`il::forEachReg`]: updates max register number.
185 185
fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) {
lib/std/lang/gen/regalloc/spill.rad +6 -6
155 155
    // a callee-saved register for cross-call values.
156 156
    while bitset::count(&calleeClass) > numCalleeSaved {
157 157
        let mut it = bitset::iter(&calleeClass);
158 158
        if let reg = bitset::iterNext(&mut it) {
159 159
            bitset::clear(&mut calleeClass, reg);
160 -
            bitset::set(&mut spilled, reg);
160 +
            bitset::put(&mut spilled, reg);
161 161
        } else {
162 162
            panic "spill: count > 0 but no set bits found";
163 163
        }
164 164
    }
165 165
224 224
        }
225 225
        c.entries[j] = key;
226 226
    }
227 227
    let toSpill = c.n if excess > c.n else excess;
228 228
    for i in 0..toSpill {
229 -
        bitset::set(spilled, c.entries[i].reg);
229 +
        bitset::put(spilled, c.entries[i].reg);
230 230
        bitset::clear(source, c.entries[i].reg);
231 231
    }
232 232
}
233 233
234 234
/// Collect all values from a bitset into a candidates buffer with their costs.
235 -
fn collectCandidates(set: *bitset::Bitset, costs: *[SpillCost]) -> Candidates {
235 +
fn collectCandidates(bs: *bitset::Bitset, costs: *[SpillCost]) -> Candidates {
236 236
    let mut c = Candidates { entries: undefined, n: 0 };
237 -
    let mut it = bitset::iter(set);
237 +
    let mut it = bitset::iter(bs);
238 238
    while let reg = bitset::iterNext(&mut it) {
239 239
        assert c.n < MAX_CANDIDATES, "collectCandidates: too many live values";
240 240
        if reg < costs.len {
241 241
            c.entries[c.n] = CostEntry { reg, cost: costs[reg].defs + costs[reg].uses };
242 242
            c.n += 1;
301 301
    }
302 302
    // Mark crossing values that remain after spilling as callee-saved class.
303 303
    let mut it2 = bitset::iter(live);
304 304
    while let n = bitset::iterNext(&mut it2) {
305 305
        if not isCallDst(callDst, n) {
306 -
            bitset::set(calleeClass, n);
306 +
            bitset::put(calleeClass, n);
307 307
        }
308 308
    }
309 309
}
310 310
311 311
/// Callback for [`il::forEachReg`]: increments use count for register.
315 315
    ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight;
316 316
}
317 317
318 318
/// Callback for [`il::forEachReg`]: adds register to live set.
319 319
fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) {
320 -
    bitset::set(ctx as *mut bitset::Bitset, reg.n);
320 +
    bitset::put(ctx as *mut bitset::Bitset, reg.n);
321 321
}
322 322
323 323
/// Check if a register is spilled.
324 324
export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool {
325 325
    if reg.n >= info.maxReg {
lib/std/lang/lower.rad +22 -22
6708 6708
        emit(self, il::Instr::Zext { typ: extType, dst, val });
6709 6709
    }
6710 6710
    return il::Val::Reg(dst);
6711 6711
}
6712 6712
6713 +
/// Lower a global value symbol.
6714 +
fn lowerGlobalValue(self: *mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val {
6715 +
    // Function pointer reference: return the function's address directly.
6716 +
    // Functions have no separate storage cell in the data section.
6717 +
    if let case resolver::Type::Fn(_) = ty {
6718 +
        return il::Val::Reg(emitFnAddr(self, sym));
6719 +
    }
6720 +
    let src = emitDataAddr(self, sym);
6721 +
6722 +
    return emitRead(self, src, 0, ty);
6723 +
}
6724 +
6713 6725
/// Lower an identifier that refers to a global symbol.
6714 6726
fn lowerGlobalSymbol(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) {
6715 6727
    // First try to get a compile-time constant value.
6716 6728
    if let constVal = resolver::constValueEntry(self.low.resolver, node) {
6717 6729
        return try constValueToVal(self, constVal, node);
6718 6730
    }
6719 6731
    // Otherwise get the symbol.
6720 6732
    let sym = try symOf(self, node);
6721 -
    let mut ty: resolver::Type = undefined;
6722 6733
6723 6734
    match sym.data {
6724 -
        case resolver::SymbolData::Constant { type, .. } =>
6725 -
            ty = type,
6726 -
        case resolver::SymbolData::Value { type, .. } => {
6727 -
            // Function pointer reference: just return the address, don't load.
6728 -
            // Functions don't have a separate storage location holding their
6729 -
            // address; they just exist at an address in the code section.
6730 -
            if let case resolver::Type::Fn(_) = type {
6731 -
                return il::Val::Reg(emitFnAddr(self, sym));
6732 -
            }
6733 -
            ty = type;
6735 +
        case resolver::SymbolData::Constant { type, .. } => {
6736 +
            let src = emitDataAddr(self, sym);
6737 +
6738 +
            return emitRead(self, src, 0, type);
6734 6739
        }
6740 +
        case resolver::SymbolData::Value { type, .. } =>
6741 +
            return lowerGlobalValue(self, sym, type),
6735 6742
        else => throw LowerError::UnexpectedNodeValue(node),
6736 6743
    }
6737 -
    let dst = emitDataAddr(self, sym);
6738 -
6739 -
    return emitRead(self, dst, 0, ty);
6740 6744
}
6741 6745
6742 6746
/// Lower an assignment to a static variable.
6743 6747
fn lowerStaticAssign(self: *mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) {
6744 6748
    let sym = try symOf(self, target);
6794 6798
6795 6799
                return il::Val::Reg(dst);
6796 6800
            }
6797 6801
            return emitRead(self, src, 0, type);
6798 6802
        }
6799 -
        case resolver::SymbolData::Value { type, .. } => {
6800 -
            // Function pointer reference.
6801 -
            if let case resolver::Type::Fn(_) = type {
6802 -
                return il::Val::Reg(emitFnAddr(self, sym));
6803 -
            }
6804 -
            throw LowerError::ExpectedFunction;
6805 -
        }
6806 -
        else => throw LowerError::UnexpectedNodeValue(node),
6803 +
        case resolver::SymbolData::Value { type, .. } =>
6804 +
            return lowerGlobalValue(self, sym, type),
6805 +
        else =>
6806 +
            throw LowerError::UnexpectedNodeValue(node),
6807 6807
    }
6808 6808
}
6809 6809
6810 6810
/// Lower an expression AST node to an IL value.
6811 6811
/// This is the main expression dispatch, all expression nodes go through here.
lib/std/lang/parser.rad +10 -0
620 620
    match node.value {
621 621
        case ast::NodeValue::Ident(_) =>
622 622
            return true,
623 623
        case ast::NodeValue::FieldAccess(_) =>
624 624
            return true,
625 +
        case ast::NodeValue::ScopeAccess(_) =>
626 +
            return true,
625 627
        case ast::NodeValue::Subscript { .. } =>
626 628
            return true,
627 629
        case ast::NodeValue::Deref(_) =>
628 630
            return true,
629 631
        else =>
984 986
            if consume(p, scanner::TokenKind::Mut) {
985 987
                return try parseLet(p, true);
986 988
            }
987 989
            return try parseLet(p, false);
988 990
        }
991 +
        case scanner::TokenKind::Set => {
992 +
            advance(p);
993 +
            let stmt = try parseExprStmt(p);
994 +
            if let case ast::NodeValue::Assign(_) = stmt.value {
995 +
                return stmt;
996 +
            }
997 +
            throw failParsing(p, "expected assignment after `set`");
998 +
        }
989 999
        case scanner::TokenKind::Constant => {
990 1000
            return try parseConst(p, attrs);
991 1001
        }
992 1002
        case scanner::TokenKind::Static => {
993 1003
            return try parseStatic(p, attrs);
lib/std/lang/parser/tests.rad +9 -0
2220 2220
            else throw testing::TestError::Failed;
2221 2221
        let case ast::NodeValue::BinOp(bin) = node.right.value
2222 2222
            else throw testing::TestError::Failed;
2223 2223
        try testing::expect(bin.op == ast::BinaryOp::Add);
2224 2224
    }
2225 +
    {
2226 +
        let assign = try! parseStmtStr("set module::VAR = 1;");
2227 +
        let case ast::NodeValue::Assign(node) = assign.value
2228 +
            else throw testing::TestError::Failed;
2229 +
        let case ast::NodeValue::ScopeAccess(access) = node.left.value
2230 +
            else throw testing::TestError::Failed;
2231 +
        try expectIdent(access.parent, "module");
2232 +
        try expectIdent(access.child, "VAR");
2233 +
    }
2225 2234
}
2226 2235
2227 2236
/// Test parsing basic arithmetic binary operators (+, -, *, /, %).
2228 2237
@test fn testParseBinOpArithmetic() throws (testing::TestError) {
2229 2238
    let add = try! parseExprStr("a + b");
lib/std/lang/resolver.rad +24 -0
5694 5694
        }
5695 5695
        case ast::NodeValue::FieldAccess(access) => {
5696 5696
            let _ = try infer(self, access.parent);
5697 5697
            return try canBorrowMutFrom(self, access.parent);
5698 5698
        }
5699 +
        case ast::NodeValue::ScopeAccess(_) => {
5700 +
            // Module-qualified access to a top-level symbol. A `static`
5701 +
            // binds as a mutable value; a `constant` does not.
5702 +
            let _ = try infer(self, node);
5703 +
            let sym = nodeData(self, node).sym
5704 +
                else return false;
5705 +
5706 +
            if let case SymbolData::Value { mutable, .. } = sym.data {
5707 +
                return mutable;
5708 +
            }
5709 +
            return false;
5710 +
        }
5699 5711
        case ast::NodeValue::Subscript { container, .. } => {
5700 5712
            let containerTy = try infer(self, container);
5701 5713
            // Subscript auto-derefs pointers, so check the actual indexed type.
5702 5714
            let subjectTy = autoDeref(containerTy);
5703 5715
5712 5724
        case ast::NodeValue::ArrayLit(_),
5713 5725
             ast::NodeValue::ArrayRepeatLit(_) =>
5714 5726
        {
5715 5727
            return true;
5716 5728
        }
5729 +
        case ast::NodeValue::Call(_) => {
5730 +
            // A call returning `*mut T` (or `&mut [T]`) yields a
5731 +
            // mutable place. Non-pointer returns cannot be mutably borrowed.
5732 +
            let ty = try infer(self, node);
5733 +
            if let case Type::Pointer { mutable, .. } = ty {
5734 +
                return mutable;
5735 +
            }
5736 +
            if let case Type::Slice { mutable, .. } = ty {
5737 +
                return mutable;
5738 +
            }
5739 +
            return false;
5740 +
        }
5717 5741
        case ast::NodeValue::Deref(inner) => {
5718 5742
            let innerTy = try infer(self, inner);
5719 5743
5720 5744
            if let case Type::Pointer { mutable, .. } = innerTy {
5721 5745
                return mutable;
lib/std/lang/resolver/tests.rad +42 -0
3399 3399
    let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }";
3400 3400
    let result = try resolveProgramStr(&mut a, program);
3401 3401
    try expectNoErrors(&result);
3402 3402
}
3403 3403
3404 +
/// Test borrowing mutably from a field access on a call returning `*mut`.
3405 +
@test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) {
3406 +
    let mut a = testResolver();
3407 +
    let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }";
3408 +
    let result = try resolveProgramStr(&mut a, program);
3409 +
    try expectNoErrors(&result);
3410 +
}
3411 +
3412 +
/// Test that calls returning immutable pointers cannot be mutably borrowed.
3413 +
@test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) {
3414 +
    let mut a = testResolver();
3415 +
    let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { let b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }";
3416 +
    let result = try resolveProgramStr(&mut a, program);
3417 +
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3418 +
}
3419 +
3420 +
/// Test borrowing mutably from a public static through scope access.
3421 +
@test fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) {
3422 +
    let mut a = testResolver();
3423 +
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3424 +
3425 +
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3426 +
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena);
3427 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; *p = 7; }", &mut arena);
3428 +
3429 +
    let result = try resolveModuleTree(&mut a, rootId);
3430 +
    try expectNoErrors(&result);
3431 +
}
3432 +
3433 +
/// Test that constants through scope access cannot be mutably borrowed.
3434 +
@test fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) {
3435 +
    let mut a = testResolver();
3436 +
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3437 +
3438 +
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3439 +
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena);
3440 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; *p = 9; }", &mut arena);
3441 +
3442 +
    let result = try resolveModuleTree(&mut a, rootId);
3443 +
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3444 +
}
3445 +
3404 3446
/// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer.
3405 3447
@test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) {
3406 3448
    let mut a = testResolver();
3407 3449
    let program = "fn f() { let mut x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }";
3408 3450
    let result = try resolveProgramStr(&mut a, program);
lib/std/lang/scanner.rad +3 -2
89 89
    Continue, While, For, In,
90 90
    Loop, Match, Case, Try, Catch,
91 91
    Throw, Throws, Panic, Assert,
92 92
93 93
    // Variable binding tokens.
94 -
    Let, Mut, Constant, Align,
94 +
    Let, Mut, Set, Constant, Align,
95 95
96 96
    // Module-related tokens.
97 97
    Mod, Use, Super,
98 98
99 99
    // Type or function attributes.
114 114
    /// Corresponding token.
115 115
    tok: TokenKind,
116 116
}
117 117
118 118
/// Sorted keyword table for binary search.
119 -
constant KEYWORDS: [Keyword; 50] = [
119 +
constant KEYWORDS: [Keyword; 51] = [
120 120
    { name: "align", tok: TokenKind::Align },
121 121
    { name: "and", tok: TokenKind::And },
122 122
    { name: "as", tok: TokenKind::As },
123 123
    { name: "assert", tok: TokenKind::Assert },
124 124
    { name: "bool", tok: TokenKind::Bool },
150 150
    { name: "opaque", tok: TokenKind::Opaque },
151 151
    { name: "or", tok: TokenKind::Or },
152 152
    { name: "panic", tok: TokenKind::Panic },
153 153
    { name: "record", tok: TokenKind::Record },
154 154
    { name: "return", tok: TokenKind::Return },
155 +
    { name: "set", tok: TokenKind::Set },
155 156
    { name: "static", tok: TokenKind::Static },
156 157
    { name: "super", tok: TokenKind::Super },
157 158
    { name: "throw", tok: TokenKind::Throw },
158 159
    { name: "throws", tok: TokenKind::Throws },
159 160
    { name: "trait", tok: TokenKind::Trait },
lib/std/tests.rad +2 -2
315 315
        throw testing::TestError::Failed;
316 316
    }
317 317
    try testing::expect(vec::get(&v, 3) == nil);
318 318
319 319
    let new: i32 = 999;
320 -
    try testing::expect(vec::set(&mut v, 1, &new));
320 +
    try testing::expect(vec::put(&mut v, 1, &new));
321 321
322 322
    if let ptr = vec::get(&v, 1) {
323 323
        let value: i32 = *(ptr as *i32);
324 324
        try testing::expect(value == 999);
325 325
    } else {
326 326
        throw testing::TestError::Failed;
327 327
    }
328 -
    try testing::expectNot(vec::set(&mut v, 5, &new));
328 +
    try testing::expectNot(vec::put(&mut v, 5, &new));
329 329
}
330 330
331 331
@test fn testVecCapacity() throws (testing::TestError) {
332 332
    let mut arena: [u8; 16] align(4) = undefined;
333 333
    let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32));
lib/std/vec.rad +1 -1
97 97
}
98 98
99 99
/// Set the element at the given index.
100 100
///
101 101
/// Returns false if index is out of bounds.
102 -
export fn set(vec: *mut RawVec, index: u32, elem: *opaque) -> bool {
102 +
export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool {
103 103
    if index >= vec.len {
104 104
        return false;
105 105
    }
106 106
    let off: u32 = index * vec.stride;
107 107
    let dst: *mut u8 = &mut vec.data[off];
test/tests/mutref.loop.bug.rad +5 -5
8 8
//!
9 9
//! The critical case is when the loop executes zero iterations: the merge
10 10
//! block tries to `load` through the initial integer value (not a valid
11 11
//! pointer), crashing the program.
12 12
13 -
fn set(ptr: *mut u32, val: u32) {
13 +
fn store(ptr: *mut u32, val: u32) {
14 14
    *ptr = val;
15 15
}
16 16
17 17
/// Zero-iteration loop with &mut inside the body.
18 18
/// Without the fix, `val` starts as integer 42 in SSA, but the post-loop
19 19
/// merge tries to `load` through it as if it were a pointer.
20 20
fn testZeroIter(n: u32) -> u32 {
21 21
    let mut val: u32 = 42;
22 22
    let mut i: u32 = 0;
23 23
    while i < n {
24 -
        set(&mut val, val + 1);
24 +
        store(&mut val, val + 1);
25 25
        i += 1;
26 26
    }
27 27
    return val;
28 28
}
29 29
30 30
/// Multiple iterations: accumulate via &mut pointer in a loop.
31 31
fn testMultiIter() -> u32 {
32 32
    let mut acc: u32 = 0;
33 33
    let mut i: u32 = 0;
34 34
    while i < 5 {
35 -
        set(&mut acc, acc + i);
35 +
        store(&mut acc, acc + i);
36 36
        i += 1;
37 37
    }
38 38
    return acc;
39 39
}
40 40
42 42
fn testMultipleVars() -> u32 {
43 43
    let mut a: u32 = 0;
44 44
    let mut b: u32 = 100;
45 45
    let mut i: u32 = 0;
46 46
    while i < 3 {
47 -
        set(&mut a, a + 1);
48 -
        set(&mut b, b - 1);
47 +
        store(&mut a, a + 1);
48 +
        store(&mut b, b - 1);
49 49
        i += 1;
50 50
    }
51 51
    return a + b;
52 52
}
53 53
test/tests/ref.if.bug.rad +2 -2
2 2
//! Regression test: address-of inside an if branch.
3 3
//!
4 4
//! When `&mut var` appears in only one branch of an if/else, the merge
5 5
//! block's phi merges the original integer value with a stack pointer.
6 6
7 -
fn set(ptr: *mut u32, val: u32) {
7 +
fn store(ptr: *mut u32, val: u32) {
8 8
    *ptr = val;
9 9
}
10 10
11 11
fn testIfBranch(cond: bool) -> u32 {
12 12
    let mut val: u32 = 42;
13 13
    if cond {
14 -
        set(&mut val, 99);
14 +
        store(&mut val, 99);
15 15
    }
16 16
    return val;
17 17
}
18 18
19 19
@default fn main() -> i32 {
test/tests/ref.mut.ptr.rad +3 -3
1 1
//! returns: 84
2 2
//! Test mutable pointer references.
3 3
4 -
fn set(ptr: *mut i32) {
4 +
fn store(ptr: *mut i32) {
5 5
    *ptr = 42;
6 6
}
7 7
8 8
@default fn main() -> i32 {
9 9
    let mut a: i32 = 0;
10 10
    let p: *mut i32 = &mut a;
11 -
    set(p);
11 +
    store(p);
12 12
13 13
    let mut b: i32 = 0;
14 -
    set(&mut b);
14 +
    store(&mut b);
15 15
16 16
    return a + b;
17 17
}
test/tests/set.keyword.rad added +28 -0
1 +
//! returns: 0
2 +
//! Test the `set` keyword for assignment statements.
3 +
4 +
record Point {
5 +
    x: i32,
6 +
    y: i32,
7 +
}
8 +
9 +
@default fn main() -> i32 {
10 +
    let mut x: i32 = 1;
11 +
    set x = 2;
12 +
    if x <> 2 {
13 +
        return 1;
14 +
    }
15 +
16 +
    let mut p: Point = Point { x: 0, y: 0 };
17 +
    set p.x = 10;
18 +
    if p.x <> 10 {
19 +
        return 1;
20 +
    }
21 +
22 +
    let mut a: [i32; 3] = [0, 0, 0];
23 +
    set a[1] += 5;
24 +
    if a[1] <> 5 {
25 +
        return 1;
26 +
    }
27 +
    return 0;
28 +
}
test/tests/trait.dispatch.rad +3 -3
2 2
    n: i32,
3 3
}
4 4
5 5
trait Ops {
6 6
    fn (*Ops) get() -> i32;
7 -
    fn (*mut Ops) set(n: i32);
7 +
    fn (*mut Ops) put(n: i32);
8 8
}
9 9
10 10
instance Ops for Acc {
11 11
    fn (a: *Acc) get() -> i32 {
12 12
        return a.n;
13 13
    }
14 14
15 -
    fn (a: *mut Acc) set(n: i32) {
15 +
    fn (a: *mut Acc) put(n: i32) {
16 16
        a.n = n;
17 17
    }
18 18
}
19 19
20 20
fn dispatch(o: *mut opaque Ops) -> i32 {
21 -
    o.set(42);
21 +
    o.put(42);
22 22
    return o.get();
23 23
}
test/tests/trait.dispatch.ril +2 -2
1 1
data $"vtable::Acc::Ops" align 8 {
2 2
    fn $"Acc::get";
3 -
    fn $"Acc::set";
3 +
    fn $"Acc::put";
4 4
}
5 5
6 6
fn w32 $"Acc::get"(w64 %0) {
7 7
  @entry0
8 8
    sload w32 %1 %0 0;
9 9
    ret %1;
10 10
}
11 11
12 -
fn w64 $"Acc::set"(w64 %0, w32 %1) {
12 +
fn w64 $"Acc::put"(w64 %0, w32 %1) {
13 13
  @entry0
14 14
    store w32 %1 %0 0;
15 15
    ret;
16 16
}
17 17
vim/radiance.vim +1 -1
15 15
syntax keyword radianceTodo TODO FIXME contained containedin=radianceComment
16 16
17 17
" Keywords
18 18
syntax keyword radianceKeyword mod fn return if else while true false and or not case align static
19 19
syntax keyword radianceKeyword export break continue use loop in for match nil undefined
20 -
syntax keyword radianceKeyword let mut as register device constant log record union trait instance
20 +
syntax keyword radianceKeyword let mut set as register device constant log record union trait instance
21 21
syntax keyword radianceKeyword throws throw try catch panic assert super
22 22
syntax keyword radianceType i8 i16 i32 i64 u8 u16 u32 u64 f32 void bool bit opaque
23 23
24 24
" Double-quoted strings
25 25
syntax region radianceString start=/"/ skip=/\\"/ end=/"/ contains=radianceEscape