Simplify `for` loops that need the index

43baa2528c0799e6b2e55e10ebc2f637e38ee1d149e742fd4bab5f9841d2e4ad
Alexis Sellier committed ago 1 parent a6d3edee
lib/std/arch/rv64/emit.rad +2 -2
159 159
    );
160 160
    frame.totalSize = totalSize;
161 161
162 162
    // Build list of callee-saved registers with offsets.
163 163
    let mut offset = totalSize - (super::DWORD_SIZE * 3);
164 -
    for i in 0..super::CALLEE_SAVED.len {
164 +
    for reg, i in super::CALLEE_SAVED {
165 165
        // Check if this register is in use.
166 166
        if (usedCalleeSaved & (1 << i)) != 0 {
167 167
            frame.savedRegs[frame.savedRegsLen] = SavedReg {
168 -
                reg: super::CALLEE_SAVED[i],
168 +
                reg,
169 169
                offset,
170 170
            };
171 171
            frame.savedRegsLen += 1;
172 172
            offset -= super::DWORD_SIZE;
173 173
        }
lib/std/arch/rv64/isel.rad +7 -9
280 280
281 281
    // Move function params from arg registers to assigned registers.
282 282
    // Cross-call params may have been assigned to callee-saved registers
283 283
    // instead of their natural arg registers. Spilled params are stored
284 284
    // directly to their spill slots.
285 -
    for i in 0..func.params.len {
285 +
    for funcParam, i in func.params {
286 286
        if i < super::ARG_REGS.len {
287 -
            let param = func.params[i].value;
287 +
            let param = funcParam.value;
288 288
            let argReg = super::ARG_REGS[i];
289 289
290 290
            if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
291 291
                // Spilled parameter: store arg register to spill slot.
292 292
                emit::emitSd(s.e, argReg, super::FP, spillOffset(&s, slot));
315 315
    // By the time we enter the block, the arguments have already been
316 316
    // moved to the parameter registers by the predecessor's terminator.
317 317
318 318
    // Process each instruction, auto-committing any pending spill after each.
319 319
    let hasLocs = block.locs.len > 0;
320 -
    for i in 0..block.instrs.len {
320 +
    for instr, i in block.instrs {
321 321
        // Record debug location before emitting machine instructions.
322 322
        if hasLocs {
323 323
            emit::recordSrcLoc(s.e, block.locs[i]);
324 324
        }
325 325
        s.pendingSpill = nil;
326 -
        selectInstr(s, blockIdx, block.instrs[i], frame, func);
326 +
        selectInstr(s, blockIdx, instr, frame, func);
327 327
328 328
        // Flush the pending spill store, if any.
329 329
        if let p = s.pendingSpill {
330 330
            if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) {
331 331
                emit::emitSd(s.e, p.rd, super::FP, spillOffset(s, slot));
1000 1000
1001 1001
    // Destination registers for each arg.
1002 1002
    // Zero means the destination is spilled or skipped.
1003 1003
    let mut dsts: [super::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
1004 1004
1005 -
    for i in 0..args.len {
1005 +
    for arg, i in args {
1006 1006
        let param = block.params[i].value;
1007 1007
1008 1008
        // Spilled destinations: store directly to spill slot.
1009 1009
        // These don't participate in the parallel move algorithm.
1010 1010
        if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, param) {
1011 -
            let a = args[i];
1012 -
1013 -
            if let case il::Val::Undef = a {
1011 +
            if let case il::Val::Undef = arg {
1014 1012
                // Undefined values don't need any move.
1015 1013
            } else {
1016 -
                let rs = resolveVal(s, super::SCRATCH1, a);
1014 +
                let rs = resolveVal(s, super::SCRATCH1, arg);
1017 1015
                emit::emitSd(s.e, rs, super::FP, spillOffset(s, slot));
1018 1016
            }
1019 1017
        } else {
1020 1018
            dsts[i] = getReg(s, param);
1021 1019
        }
lib/std/arch/rv64/printer.rad +4 -4
80 80
    write(out, ")");
81 81
}
82 82
83 83
/// Write strings separated by ", ".
84 84
fn writeDelim(out: *mut sexpr::Output, parts: *[*[u8]]) {
85 -
    for i in 0..parts.len {
85 +
    for part, i in parts {
86 86
        if i > 0 {
87 87
            write(out, ", ");
88 88
        }
89 -
        write(out, parts[i]);
89 +
        write(out, part);
90 90
    }
91 91
}
92 92
93 93
/// Write mnemonic with padding for alignment.
94 94
fn writeMnem(out: *mut sexpr::Output, m: *[u8]) {
313 313
    // Package header.
314 314
    write(out, "# package `");
315 315
    write(out, pkgName);
316 316
    write(out, "`\n\n");
317 317
318 -
    for i in 0..code.len {
318 +
    for instr, i in code {
319 319
        if let name = findFunc(funcs, i) {
320 320
            write(out, "\n# ");
321 321
            write(out, name);
322 322
            write(out, "\n\n");
323 323
        }
324 -
        printInstr(out, arena, code[i]);
324 +
        printInstr(out, arena, instr);
325 325
        write(out, "\n");
326 326
    }
327 327
}
328 328
329 329
/// Find function at given instruction index.
lib/std/lang/ast/printer.rad +10 -13
105 105
fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
106 106
    if nodes.len == 0 {
107 107
        return &[];
108 108
    }
109 109
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
110 -
    for i in 0..nodes.len {
111 -
        buf[i] = toExpr(a, nodes[i]);
110 +
    for node, i in nodes {
111 +
        buf[i] = toExpr(a, node);
112 112
    }
113 113
    return buf;
114 114
}
115 115
116 116
/// Convert an optional node to an expression, or return placeholder.
141 141
fn prongListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
142 142
    if nodes.len == 0 {
143 143
        return &[];
144 144
    }
145 145
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
146 -
    for i in 0..nodes.len {
147 -
        let prong = nodes[i];
146 +
    for prong, i in nodes {
148 147
        match prong.value {
149 148
            case super::NodeValue::MatchProng(p) => {
150 149
                buf[i] = prongToExpr(a, p);
151 150
            }
152 151
            else => {
192 191
fn fieldListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
193 192
    if nodes.len == 0 {
194 193
        return &[];
195 194
    }
196 195
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
197 -
    for i in 0..nodes.len {
198 -
        let node = nodes[i];
196 +
    for node, i in nodes {
199 197
        match node.value {
200 198
            case super::NodeValue::RecordField { field, type, value } => {
201 199
                buf[i] = fieldToExpr(a, field, type, value);
202 200
            }
203 201
            else => {
217 215
fn variantListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
218 216
    if nodes.len == 0 {
219 217
        return &[];
220 218
    }
221 219
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
222 -
    for i in 0..nodes.len {
223 -
        let node = nodes[i];
220 +
    for node, i in nodes {
224 221
        match node.value {
225 222
            case super::NodeValue::UnionDeclVariant(v) => {
226 223
                buf[i] = variantToExpr(a, v.name, v.type);
227 224
            }
228 225
            else => {
259 256
        case super::NodeValue::UnOp(u) =>
260 257
            return sexpr::list(a, unOpName(u.op), &[toExpr(a, u.value)]),
261 258
        case super::NodeValue::Call(c) => {
262 259
            let buf = try! sexpr::allocExprs(a, c.args.len as u32 + 1);
263 260
            buf[0] = toExpr(a, c.callee);
264 -
            for i in 0..c.args.len { buf[i + 1] = toExpr(a, c.args[i]); }
261 +
            for arg, i in c.args { buf[i + 1] = toExpr(a, arg); }
265 262
            return sexpr::Expr::List { head: "call", tail: buf, multiline: false };
266 263
        }
267 264
        case super::NodeValue::BuiltinCall { kind, args } =>
268 265
            return sexpr::list(a, builtinName(kind), nodeListToExprs(a, &args[..])),
269 266
        case super::NodeValue::Subscript { container, index } =>
291 288
            let buf = try! sexpr::allocExprs(a, total);
292 289
            let mut idx: u32 = 0;
293 290
            if let tn = lit.typeName {
294 291
                buf[idx] = toExpr(a, tn); idx = idx + 1;
295 292
            }
296 -
            for i in 0..lit.fields.len {
297 -
                buf[idx + i] = toExpr(a, lit.fields[i]);
293 +
            for field, i in lit.fields {
294 +
                buf[idx + i] = toExpr(a, field);
298 295
            }
299 296
            return sexpr::Expr::List { head: "record-lit", tail: buf, multiline: lit.fields.len > 2 };
300 297
        }
301 298
        case super::NodeValue::RecordLitField(f) =>
302 299
            return sexpr::list(a, "field", &[toExprOpt(a, f.label), toExpr(a, f.value)]),
463 460
464 461
/// Dump the tree rooted at `root`, using the provided arena for allocation.
465 462
pub fn printTree(root: *super::Node, arena: *mut alloc::Arena) {
466 463
    match root.value {
467 464
        case super::NodeValue::Block(blk) => {
468 -
            for i in 0..blk.statements.len {
469 -
                sexpr::print(toExpr(arena, blk.statements[i]), 0);
465 +
            for stmt, i in blk.statements {
466 +
                sexpr::print(toExpr(arena, stmt), 0);
470 467
                if i < blk.statements.len - 1 { io::print("\n\n"); }
471 468
            }
472 469
            io::print("\n");
473 470
        }
474 471
        else => {
lib/std/lang/gen/regalloc/assign.rad +6 -7
74 74
    }
75 75
    // Pre-assign function parameters to argument registers.
76 76
    // Cross-call params are NOT pre-assigned here; they will be allocated
77 77
    // to callee-saved registers by the normal path, and isel emits moves
78 78
    // from the arg register to the assigned register at function entry.
79 -
    for i in 0..func.params.len {
79 +
    for param, i in func.params {
80 80
        if i < config.argRegs.len {
81 -
            if not bitset::contains(&spillInfo.calleeClass, func.params[i].value.n) {
82 -
                assignments[func.params[i].value.n] = config.argRegs[i];
81 +
            if not bitset::contains(&spillInfo.calleeClass, param.value.n) {
82 +
                assignments[param.value.n] = config.argRegs[i];
83 83
            }
84 84
        }
85 85
    }
86 86
    let blkEnd = try alloc::allocSlice(arena, @sizeOf(RegMap), @alignOf(RegMap), blockCount) as *mut [RegMap];
87 87
    for i in 0..blockCount {
144 144
                assignments[p.value.n] = rallocReg(&mut current, &mut usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo);
145 145
            }
146 146
        }
147 147
148 148
        // Process each instruction.
149 -
        for i in 0..block.instrs.len {
150 -
            let instr = block.instrs[i];
149 +
        for instr, i in block.instrs {
151 150
            let mut ctx = InstrCtx {
152 151
                current: &mut current,
153 152
                usedRegs: &mut usedRegs,
154 153
                func,
155 154
                live,
174 173
    }
175 174
    // Compute bitmask of used callee-saved registers.
176 175
    let mut usedCalleeSaved: u32 = 0;
177 176
    for i in 0..maxReg {
178 177
        if let phys = assignments[i] {
179 -
            for j in 0..config.calleeSaved.len {
180 -
                if phys == config.calleeSaved[j] {
178 +
            for saved, j in config.calleeSaved {
179 +
                if phys == saved {
181 180
                    usedCalleeSaved |= (1 << j);
182 181
                }
183 182
            }
184 183
        }
185 184
    }
lib/std/lang/il/printer.rad +8 -8
178 178
}
179 179
180 180
/// Write a comma-separated argument list in parentheses.
181 181
fn writeArgs(out: *mut sexpr::Output, a: *mut alloc::Arena, args: *[super::Val]) {
182 182
    write(out, "(");
183 -
    for i in 0..args.len {
183 +
    for arg, i in args {
184 184
        if i > 0 {
185 185
            write(out, ", ");
186 186
        }
187 -
        writeVal(out, a, args[i]);
187 +
        writeVal(out, a, arg);
188 188
    }
189 189
    write(out, ")");
190 190
}
191 191
192 192
/// Write a typed parameter (e.g., `w32 %0`).
196 196
    writeReg(out, a, param.value);
197 197
}
198 198
199 199
/// Write a comma-separated parameter list.
200 200
fn writeParams(out: *mut sexpr::Output, a: *mut alloc::Arena, params: *[super::Param]) {
201 -
    for i in 0..params.len {
201 +
    for param, i in params {
202 202
        if i > 0 {
203 203
            write(out, ", ");
204 204
        }
205 -
        writeParam(out, a, params[i]);
205 +
        writeParam(out, a, param);
206 206
    }
207 207
}
208 208
209 209
//////////////////////////
210 210
// Instruction printing //
538 538
//////////////////////
539 539
540 540
/// Print a program.
541 541
pub fn printProgram(out: *mut sexpr::Output, a: *mut alloc::Arena, program: *super::Program) {
542 542
    // Data declarations.
543 -
    for i in 0..program.data.len {
544 -
        writeData(out, a, program.data[i]);
543 +
    for data, i in program.data {
544 +
        writeData(out, a, data);
545 545
        if i < program.data.len - 1 or program.fns.len > 0 {
546 546
            write(out, "\n");
547 547
        }
548 548
    }
549 549
    // Functions.
550 -
    for i in 0..program.fns.len {
551 -
        writeFn(out, a, program.fns[i]);
550 +
    for func, i in program.fns {
551 +
        writeFn(out, a, func);
552 552
        if i < program.fns.len - 1 {
553 553
            write(out, "\n");
554 554
        }
555 555
    }
556 556
}
lib/std/lang/lower.rad +22 -33
1065 1065
    }
1066 1066
1067 1067
    // Fill inherited method slots from supertraits.
1068 1068
    // These methods were already lowered as part of the supertrait instance
1069 1069
    // declarations and use the same `Type::method` qualified name.
1070 -
    for i in 0..traitInfo.methods.len {
1070 +
    for method, i in traitInfo.methods {
1071 1071
        if not methodNameSet[i] {
1072 -
            let mName = traitInfo.methods[i].name;
1073 -
            methodNames[i] = instanceMethodName(self, nil, typeName, mName);
1072 +
            methodNames[i] = instanceMethodName(self, nil, typeName, method.name);
1074 1073
        }
1075 1074
    }
1076 1075
1077 1076
    // Create v-table in data section, used for dynamic dispatch.
1078 1077
    let vName = vtableName(self, nil, typeName, tName);
1449 1448
    recInfo: resolver::RecordType,
1450 1449
    dataPrefix: *[u8],
1451 1450
    b: *mut DataValueBuilder
1452 1451
) throws (LowerError) {
1453 1452
    let layout = recInfo.layout;
1454 -
    for i in 0..args.len {
1455 -
        let argNode = args[i];
1453 +
    for argNode, i in args {
1456 1454
        let mut valueNode = argNode;
1457 1455
        if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value {
1458 1456
            valueNode = fieldLit.value;
1459 1457
        }
1460 1458
        let fieldInfo = recInfo.fields[i];
2823 2821
    }
2824 2822
    let newArgs = try! alloc::allocSlice(
2825 2823
        self.low.arena, @sizeOf(il::Val), @alignOf(il::Val), capacity
2826 2824
    ) as *mut [il::Val];
2827 2825
2828 -
    for i in 0..args.len {
2829 -
        newArgs[i] = args[i];
2826 +
    for arg, i in args {
2827 +
        newArgs[i] = arg;
2830 2828
    }
2831 2829
    for i in args.len..capacity {
2832 2830
        newArgs[i] = il::Val::Undef;
2833 2831
    }
2834 2832
    return newArgs;
3177 3175
    let mut blocks: [BlockId; 32] = undefined;
3178 3176
    let mut cases: *mut [il::SwitchCase] = &mut [];
3179 3177
    let mut defaultIdx: u32 = 0;
3180 3178
    let entry = currentBlock(self);
3181 3179
3182 -
    for i in 0..prongs.len {
3183 -
        let p = prongs[i];
3180 +
    for p, i in prongs {
3184 3181
        let case ast::NodeValue::MatchProng(prong) = p.value
3185 3182
            else throw LowerError::UnexpectedNodeValue(p);
3186 3183
3187 3184
        match prong.arm {
3188 3185
            case ast::ProngArm::Binding(_), ast::ProngArm::Else => {
3210 3207
        defaultTarget: blocks[defaultIdx].n,
3211 3208
        defaultArgs: &mut [],
3212 3209
        cases: &mut cases[..]
3213 3210
    });
3214 3211
3215 -
    for i in 0..prongs.len {
3216 -
        let p = prongs[i];
3212 +
    for p, i in prongs {
3217 3213
        let case ast::NodeValue::MatchProng(prong) = p.value
3218 3214
            else throw LowerError::UnexpectedNodeValue(p);
3219 3215
3220 3216
        try switchToAndSeal(self, blocks[i]);
3221 3217
        try lowerNode(self, prong.body);
3295 3291
    // Fallback: chained branches.
3296 3292
    let firstArm = try createBlock(self, "arm");
3297 3293
    try emitJmp(self, firstArm);
3298 3294
    try switchToAndSeal(self, firstArm);
3299 3295
3300 -
    for i in 0..prongs.len {
3296 +
    for prongNode, i in prongs {
3301 3297
        let prongScope = enterVarScope(self);
3302 -
        let prongNode = prongs[i];
3303 3298
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
3304 3299
            else panic "lowerMatch: expected match prong";
3305 3300
3306 3301
        let isLastArm = i + 1 == prongs.len;
3307 3302
        let hasGuard = prong.guard != nil;
4215 4210
    let cases = try! alloc::allocSlice(
4216 4211
        self.low.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32
4217 4212
    ) as *mut [il::SwitchCase];
4218 4213
4219 4214
    let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined;
4220 -
    for i in 0..unionInfo.variants.len {
4221 -
        if unionInfo.variants[i].valueType == resolver::Type::Void {
4215 +
    for variant, i in unionInfo.variants {
4216 +
        if variant.valueType == resolver::Type::Void {
4222 4217
            cases[i] = il::SwitchCase {
4223 4218
                value: i as i64,
4224 4219
                target: mergeBlock.n,
4225 4220
                args: trueArgs
4226 4221
            };
4256 4251
        }
4257 4252
    }
4258 4253
    let valOffset = unionInfo.valOffset as i32;
4259 4254
4260 4255
    // Emit payload comparison blocks for non-void variants.
4261 -
    for i in 0..unionInfo.variants.len {
4256 +
    for variant, i in unionInfo.variants {
4262 4257
        if let caseBlock = caseBlocks[i] {
4263 4258
            try switchToAndSeal(self, caseBlock);
4264 4259
            let payloadEq = try emitEqAtOffset(
4265 -
                self, a, b, offset + valOffset, unionInfo.variants[i].valueType
4260 +
                self, a, b, offset + valOffset, variant.valueType
4266 4261
            );
4267 4262
            try emitJmpWithArg(self, mergeBlock, payloadEq);
4268 4263
        }
4269 4264
    }
4270 4265
    // Emit unreachable block.
4395 4390
    dst: il::Reg,
4396 4391
    recInfo: *resolver::RecordType,
4397 4392
    fields: *mut [*ast::Node],
4398 4393
    offset: i32
4399 4394
) throws (LowerError) {
4400 -
    for i in 0..fields.len {
4401 -
        let fieldNode = fields[i];
4395 +
    for fieldNode, i in fields {
4402 4396
        let case ast::NodeValue::RecordLitField(field) = fieldNode.value else {
4403 4397
            throw LowerError::UnexpectedNodeValue(fieldNode);
4404 4398
        };
4405 4399
        let mut fieldIdx: u32 = i;
4406 4400
        if recInfo.labeled {
4426 4420
        throw LowerError::ExpectedRecord;
4427 4421
    };
4428 4422
    let typ = resolver::Type::Nominal(nominal);
4429 4423
    let dst = try emitReserve(self, typ);
4430 4424
4431 -
    for i in 0..args.len {
4432 -
        let argNode = args[i];
4425 +
    for argNode, i in args {
4433 4426
        // Skip `undefined` arguments.
4434 4427
        if not isUndef(argNode) {
4435 4428
            let fieldTy = recInfo.fields[i].fieldType;
4436 4429
            let argVal = try lowerExpr(self, argNode);
4437 4430
            try emitStore(self, dst, recInfo.fields[i].offset, fieldTy, argVal);
4452 4445
    };
4453 4446
    let elemTy = *arrInfo.item;
4454 4447
    let elemLayout = resolver::getTypeLayout(elemTy);
4455 4448
    let dst = try emitReserve(self, typ);
4456 4449
4457 -
    for i in 0..elements.len {
4458 -
        let elemNode = elements[i];
4450 +
    for elemNode, i in elements {
4459 4451
        let elemVal = try lowerExpr(self, elemNode);
4460 4452
        let offset = i * elemLayout.size;
4461 4453
4462 4454
        try emitStore(self, dst, offset as i32, elemTy, elemVal);
4463 4455
    }
4730 4722
        dst: arrayReg,
4731 4723
        size: il::Val::Imm(arraySize as i64),
4732 4724
        alignment: elemLayout.alignment
4733 4725
    });
4734 4726
    // Store each element.
4735 -
    for i in 0..elements.len {
4736 -
        let elemNode = elements[i];
4727 +
    for elemNode, i in elements {
4737 4728
        let elemVal = try lowerExpr(self, elemNode);
4738 4729
        let offset = i * elemLayout.size;
4739 4730
4740 4731
        try emitStore(self, arrayReg, offset as i32, *elemTy, elemVal);
4741 4732
    }
5890 5881
    let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined;
5891 5882
    let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined;
5892 5883
    let mut cases: *mut [il::SwitchCase] = &mut [];
5893 5884
    let mut defaultIdx: ?u32 = nil;
5894 5885
5895 -
    for i in 0..catches.len {
5896 -
        let clauseNode = catches[i];
5886 +
    for clauseNode, i in catches {
5897 5887
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value
5898 5888
            else panic "lowerMultiCatch: expected CatchClause";
5899 5889
5900 5890
        blocks[i] = try createBlock(self, "catch");
5901 5891
        addPredecessor(self, blocks[i], entry);
5931 5921
        defaultArgs: &mut [],
5932 5922
        cases
5933 5923
    });
5934 5924
5935 5925
    // Second pass: emit each catch clause body.
5936 -
    for i in 0..catches.len {
5937 -
        let clauseNode = catches[i];
5926 +
    for clauseNode, i in catches {
5938 5927
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value
5939 5928
            else panic "lowerMultiCatch: expected CatchClause";
5940 5929
5941 5930
        try switchToAndSeal(self, blocks[i]);
5942 5931
        let savedVarsLen = enterVarScope(self);
6245 6234
6246 6235
    // Data pointer is the receiver (first argument after hidden return param).
6247 6236
    args[argOffset] = il::Val::Reg(dataReg);
6248 6237
6249 6238
    // Lower user arguments.
6250 -
    for i in 0..call.args.len {
6251 -
        args[i + 1 + argOffset] = try lowerExpr(self, call.args[i]);
6239 +
    for arg, i in call.args {
6240 +
        args[i + 1 + argOffset] = try lowerExpr(self, arg);
6252 6241
    }
6253 6242
6254 6243
    // Allocate the return buffer when needed.
6255 6244
    if returnParam {
6256 6245
        if methodFnType.throwList.len > 0 {
6397 6386
    // Lower function value and arguments, reserving an extra slot for the
6398 6387
    // hidden return buffer when needed.
6399 6388
    let callee = try lowerCallee(self, call.callee);
6400 6389
    let offset: u32 = 1 if returnParam else 0;
6401 6390
    let args = try allocVals(self, call.args.len + offset);
6402 -
    for i in 0..call.args.len {
6403 -
        args[i + offset] = try lowerExpr(self, call.args[i]);
6391 +
    for arg, i in call.args {
6392 +
        args[i + offset] = try lowerExpr(self, arg);
6404 6393
    }
6405 6394
6406 6395
    // Allocate the return buffer when needed.
6407 6396
    if returnParam {
6408 6397
        if fnInfo.throwList.len > 0 {
lib/std/lang/module.rad +2 -2
430 430
pub fn trimExtension(path: *[u8]) -> ?*[u8] {
431 431
    if path.len < SOURCE_EXT.len {
432 432
        return nil;
433 433
    }
434 434
    let extStart = path.len - SOURCE_EXT.len;
435 -
    for i in 0..SOURCE_EXT.len {
436 -
        if path[extStart + i] != SOURCE_EXT[i] {
435 +
    for ext, i in SOURCE_EXT {
436 +
        if path[extStart + i] != ext {
437 437
            return nil;
438 438
        }
439 439
    }
440 440
    return &path[..extStart];
441 441
}
lib/std/lang/module/printer.rad +1 -1
37 37
    let idText = formatId(a, entry.id as u32);
38 38
    let path = super::moduleQualifiedPath(entry);
39 39
    let mut pathBuf: *[sexpr::Expr] = &[];
40 40
    if path.len > 0 {
41 41
        let buf = try! sexpr::allocExprs(a, path.len);
42 -
        for i in 0..path.len { buf[i] = sexpr::sym(path[i]); }
42 +
        for seg, i in path { buf[i] = sexpr::sym(seg); }
43 43
        pathBuf = buf;
44 44
    }
45 45
46 46
    let mut childBuf: *[sexpr::Expr] = &[];
47 47
    if entry.childrenLen > 0 {
lib/std/lang/resolver.rad +20 -27
2978 2978
/// assignable to its corresponding field type.
2979 2979
fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType)
2980 2980
    throws (ResolveError)
2981 2981
{
2982 2982
    try checkRecordArity(self, args, recInfo, node);
2983 -
    for i in 0..args.len {
2984 -
        let arg = args[i];
2983 +
    for arg, i in args {
2985 2984
        let fieldType = recInfo.fields[i].fieldType;
2986 2985
        try checkAssignable(self, arg, fieldType);
2987 2986
    }
2988 2987
}
2989 2988
3505 3504
            throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
3506 3505
                expected: tm.fnType.paramTypes.len as u32,
3507 3506
                actual: sig.params.len,
3508 3507
            }));
3509 3508
        }
3510 -
        for j in 0..sig.params.len {
3511 -
            let paramNode = sig.params[j];
3509 +
        for paramNode, j in sig.params {
3512 3510
            let case ast::NodeValue::FnParam(param) = paramNode.value
3513 3511
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
3514 3512
            let instanceParamTy = try resolveValueType(self, param.type);
3515 3513
            if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) {
3516 3514
                throw emitTypeMismatch(self, paramNode, TypeMismatch {
3533 3531
            throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
3534 3532
                expected: tm.fnType.throwList.len as u32,
3535 3533
                actual: sig.throwList.len,
3536 3534
            }));
3537 3535
        }
3538 -
        for j in 0..sig.throwList.len {
3539 -
            let instanceThrowTy = try resolveValueType(self, sig.throwList[j]);
3536 +
        for throwNode, j in sig.throwList {
3537 +
            let instanceThrowTy = try resolveValueType(self, throwNode);
3540 3538
            if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) {
3541 -
                throw emitTypeMismatch(self, sig.throwList[j], TypeMismatch {
3539 +
                throw emitTypeMismatch(self, throwNode, TypeMismatch {
3542 3540
                    expected: *tm.fnType.throwList[j],
3543 3541
                    actual: instanceThrowTy,
3544 3542
                });
3545 3543
            }
3546 3544
        }
3581 3579
3582 3580
    // Fill inherited method slots from supertrait instances.
3583 3581
    for superTrait in traitInfo.supertraits {
3584 3582
        let superInst = findInstance(self, superTrait, concreteType)
3585 3583
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
3586 -
        for mi in 0..superTrait.methods.len {
3587 -
            let merged = findTraitMethod(traitInfo, superTrait.methods[mi].name)
3584 +
        for superMethod, mi in superTrait.methods {
3585 +
            let merged = findTraitMethod(traitInfo, superMethod.name)
3588 3586
                else panic "resolveInstanceDecl: inherited method not found";
3589 3587
            if not covered[merged.index] {
3590 3588
                entry.methods[merged.index] = superInst.methods[mi];
3591 3589
                covered[merged.index] = true;
3592 3590
            }
3593 3591
        }
3594 3592
    }
3595 3593
3596 3594
    // Check that all trait methods are implemented.
3597 -
    for i in 0..traitInfo.methods.len {
3595 +
    for method, i in traitInfo.methods {
3598 3596
        if not covered[i] {
3599 -
            throw emitError(self, node, ErrorKind::MissingTraitMethod(traitInfo.methods[i].name));
3597 +
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
3600 3598
        }
3601 3599
    }
3602 3600
    self.instances[self.instancesLen] = entry;
3603 3601
    self.instancesLen += 1;
3604 3602
3698 3696
3699 3697
    if decl.variants.len > MAX_UNION_VARIANTS {
3700 3698
        panic "resolveUnionBody: maximum union variants exceeded";
3701 3699
    }
3702 3700
    let mut iota: u32 = 0;
3703 -
    for i in 0..decl.variants.len {
3704 -
        let variantNode = decl.variants[i];
3701 +
    for variantNode, i in decl.variants {
3705 3702
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
3706 3703
            else panic "resolveUnionBody: invalid union variant";
3707 3704
        let variantName = try nodeName(self, variantDecl.name);
3708 3705
        // Resolve the variant's payload type if present.
3709 3706
        let mut variantType = Type::Void;
4216 4213
            }
4217 4214
        }
4218 4215
    }
4219 4216
    // Check that all variants are covered.
4220 4217
    if not state.catchAll {
4221 -
        for i in 0..info.variants.len {
4218 +
        for variant, i in info.variants {
4222 4219
            if not covered[i] {
4223 4220
                throw emitError(
4224 -
                    self, node, ErrorKind::UnionMatchNonExhaustive(info.variants[i].name)
4221 +
                    self, node, ErrorKind::UnionMatchNonExhaustive(variant.name)
4225 4222
                );
4226 4223
            }
4227 4224
        }
4228 4225
    } else if coveredCount == info.variants.len as u32 {
4229 4226
        throw emitError(self, node, ErrorKind::UnreachableElse);
4451 4448
    match pattern.value {
4452 4449
        case ast::NodeValue::Call(call) => {
4453 4450
            // Unlabeled patterns: `S(x, y)`.
4454 4451
            try checkRecordArity(self, call.args, recInfo, pattern);
4455 4452
4456 -
            for i in 0..call.args.len {
4457 -
                let binding = call.args[i];
4453 +
            for binding, i in call.args {
4458 4454
                let fieldType = recInfo.fields[i].fieldType;
4459 4455
                try bindPatternVar(self, binding, fieldType, matchBy);
4460 4456
            }
4461 4457
        }
4462 4458
        case ast::NodeValue::RecordLit(lit) => {
4658 4654
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
4659 4655
            expected: info.paramTypes.len as u32,
4660 4656
            actual: call.args.len,
4661 4657
        }));
4662 4658
    }
4663 -
    for i in 0..call.args.len {
4664 -
        let argNode = call.args[i];
4659 +
    for argNode, i in call.args {
4665 4660
        let expectedTy = *info.paramTypes[i];
4666 4661
4667 4662
        try checkAssignable(self, argNode, expectedTy);
4668 4663
    }
4669 4664
}
4880 4875
    }
4881 4876
}
4882 4877
4883 4878
/// Find a record field by name.
4884 4879
fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 {
4885 -
    for i in 0..s.fields.len {
4886 -
        if let name = s.fields[i].name {
4880 +
    for field, i in s.fields {
4881 +
        if let name = field.name {
4887 4882
            if name == fieldName {
4888 4883
                return i;
4889 4884
            }
4890 4885
        }
4891 4886
    }
5018 5013
        let missingName = recordType.fields[lit.fields.len].name else panic;
5019 5014
        throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
5020 5015
    }
5021 5016
5022 5017
    // Fields must be in declaration order.
5023 -
    for idx in 0..lit.fields.len {
5024 -
        let fieldNode = lit.fields[idx];
5018 +
    for fieldNode, idx in lit.fields {
5025 5019
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
5026 5020
            else panic "resolveRecordLit: expected field node value";
5027 5021
        let label = fieldArg.label
5028 5022
            else panic "resolveRecordLit: expected labeled field";
5029 5023
        let fieldName = try nodeName(self, label);
5075 5069
            }));
5076 5070
        }
5077 5071
    }
5078 5072
5079 5073
    // Fields must be in declaration order.
5080 -
    for idx in 0..lit.fields.len {
5081 -
        let fieldNode = lit.fields[idx];
5074 +
    for fieldNode, idx in lit.fields {
5082 5075
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
5083 5076
            else panic "resolveAnonRecordLit: expected field node value";
5084 5077
        let label = fieldArg.label
5085 5078
            else panic "resolveAnonRecordLit: expected labeled field";
5086 5079
        let fieldName = try nodeName(self, label);
5696 5689
        if let typeNode = clause.typeNode {
5697 5690
            // Typed catch clause: validate against callee's throw list.
5698 5691
            let errTy = try infer(self, typeNode);
5699 5692
            let mut foundIdx: ?u32 = nil;
5700 5693
5701 -
            for j in 0..calleeInfo.throwList.len {
5702 -
                if errTy == *calleeInfo.throwList[j] {
5694 +
            for throwType, j in calleeInfo.throwList {
5695 +
                if errTy == *throwType {
5703 5696
                    foundIdx = j;
5704 5697
                    break;
5705 5698
                }
5706 5699
            }
5707 5700
            let idx = foundIdx else {
lib/std/lang/resolver/printer.rad +4 -4
110 110
            io::print("?");
111 111
            printTypeBody(*inner, brief);
112 112
        }
113 113
        case super::Type::Fn(fnType) => {
114 114
            io::print("fn(");
115 -
            for i in 0..fnType.paramTypes.len {
115 +
            for paramType, i in fnType.paramTypes {
116 116
                if i > 0 {
117 117
                    io::print(", ");
118 118
                }
119 -
                printTypeName(*fnType.paramTypes[i]);
119 +
                printTypeName(*paramType);
120 120
            }
121 121
            io::print(")");
122 122
            io::print(" -> ");
123 123
            printTypeName(*fnType.returnType);
124 124
            if fnType.throwList.len > 0 {
125 125
                io::print(" throws ");
126 -
                for i in 0..fnType.throwList.len {
126 +
                for throwType, i in fnType.throwList {
127 127
                    if i > 0 {
128 128
                        io::print(", ");
129 129
                    }
130 -
                    printTypeName(*fnType.throwList[i]);
130 +
                    printTypeName(*throwType);
131 131
                }
132 132
            }
133 133
        }
134 134
        case super::Type::Nominal(info) => {
135 135
            if brief {
lib/std/lang/sexpr.rad +9 -9
45 45
pub fn allocItems(a: *mut alloc::Arena, items: *[Expr]) -> *[Expr] {
46 46
    if items.len == 0 {
47 47
        return &[];
48 48
    }
49 49
    let buf = try! allocExprs(a, items.len);
50 -
    for i in 0..items.len {
51 -
        buf[i] = items[i];
50 +
    for item, i in items {
51 +
        buf[i] = item;
52 52
    }
53 53
    return buf;
54 54
}
55 55
56 56
/// Allocate and copy prefix items followed by suffix items.
58 58
    let total = prefix.len + suffix.len;
59 59
    if total == 0 {
60 60
        return &[];
61 61
    }
62 62
    let buf = try! allocExprs(a, total);
63 -
    for i in 0..prefix.len {
64 -
        buf[i] = prefix[i];
63 +
    for item, i in prefix {
64 +
        buf[i] = item;
65 65
    }
66 -
    for i in 0..suffix.len {
67 -
        buf[prefix.len + i] = suffix[i];
66 +
    for item, i in suffix {
67 +
        buf[prefix.len + i] = item;
68 68
    }
69 69
    return buf;
70 70
}
71 71
72 72
/// Shorthand for creating a symbol.
177 177
            }
178 178
            write(out, ")");
179 179
        }
180 180
        case Expr::Vec { items } => {
181 181
            write(out, "[");
182 -
            for i in 0..items.len {
183 -
                if items[i] != Expr::Null {
182 +
            for item, i in items {
183 +
                if item != Expr::Null {
184 184
                    if i > 0 {
185 185
                        write(out, " ");
186 186
                    }
187 -
                    printTo(items[i], depth, out);
187 +
                    printTo(item, depth, out);
188 188
                }
189 189
            }
190 190
            write(out, "]");
191 191
        }
192 192
        case Expr::Block { name, items, children } => {