Use conditional expressions throughout compiler

7c8cce71d571e106a78617403a2a1ba84b44decca0b04dd55df99b73d67529a5
These were made available be the recent seed update.
Alexis Sellier committed ago 1 parent 610f0e95
lib/std/arch/rv64/isel.rad +24 -30
672 672
                selectBinOp(s, rd, rs1, b, BinOp::Add, super::SCRATCH2);
673 673
            }
674 674
        }
675 675
        case il::BinOp::Sub => {
676 676
            let rs2 = resolveVal(s, super::SCRATCH2, b);
677 -
            if typ == il::Type::W32 {
678 -
                emit::emit(s.e, encode::subw(rd, rs1, rs2));
679 -
            } else {
680 -
                emit::emit(s.e, encode::sub(rd, rs1, rs2));
681 -
            }
677 +
            emit::emit(s.e,
678 +
                encode::subw(rd, rs1, rs2)
679 +
                    if typ == il::Type::W32 else
680 +
                encode::sub(rd, rs1, rs2));
682 681
        }
683 682
        case il::BinOp::Mul => {
684 683
            let rs2 = resolveVal(s, super::SCRATCH2, b);
685 -
            if typ == il::Type::W32 {
686 -
                emit::emit(s.e, encode::mulw(rd, rs1, rs2));
687 -
            } else {
688 -
                emit::emit(s.e, encode::mul(rd, rs1, rs2));
689 -
            }
684 +
            emit::emit(s.e,
685 +
                encode::mulw(rd, rs1, rs2)
686 +
                    if typ == il::Type::W32 else
687 +
                encode::mul(rd, rs1, rs2));
690 688
        }
691 689
        case il::BinOp::Sdiv => {
692 690
            let rs2 = resolveVal(s, super::SCRATCH2, b);
693 691
            emitTrapIfZero(s, rs2);
694 -
            if typ == il::Type::W32 {
695 -
                emit::emit(s.e, encode::divw(rd, rs1, rs2));
696 -
            } else {
697 -
                emit::emit(s.e, encode::div(rd, rs1, rs2));
698 -
            }
692 +
            emit::emit(s.e,
693 +
                encode::divw(rd, rs1, rs2)
694 +
                    if typ == il::Type::W32 else
695 +
                encode::div(rd, rs1, rs2));
699 696
        }
700 697
        case il::BinOp::Udiv => {
701 698
            let rs2 = resolveVal(s, super::SCRATCH2, b);
702 699
            emitTrapIfZero(s, rs2);
703 -
            if typ == il::Type::W32 {
704 -
                emit::emit(s.e, encode::divuw(rd, rs1, rs2));
705 -
            } else {
706 -
                emit::emit(s.e, encode::divu(rd, rs1, rs2));
707 -
            }
700 +
            emit::emit(s.e,
701 +
                encode::divuw(rd, rs1, rs2)
702 +
                    if typ == il::Type::W32 else
703 +
                encode::divu(rd, rs1, rs2));
708 704
        }
709 705
        case il::BinOp::Srem => {
710 706
            let rs2 = resolveVal(s, super::SCRATCH2, b);
711 707
            emitTrapIfZero(s, rs2);
712 -
            if typ == il::Type::W32 {
713 -
                emit::emit(s.e, encode::remw(rd, rs1, rs2));
714 -
            } else {
715 -
                emit::emit(s.e, encode::rem(rd, rs1, rs2));
716 -
            }
708 +
            emit::emit(s.e,
709 +
                encode::remw(rd, rs1, rs2)
710 +
                    if typ == il::Type::W32 else
711 +
                encode::rem(rd, rs1, rs2));
717 712
        }
718 713
        case il::BinOp::Urem => {
719 714
            let rs2 = resolveVal(s, super::SCRATCH2, b);
720 715
            emitTrapIfZero(s, rs2);
721 -
            if typ == il::Type::W32 {
722 -
                emit::emit(s.e, encode::remuw(rd, rs1, rs2));
723 -
            } else {
724 -
                emit::emit(s.e, encode::remu(rd, rs1, rs2));
725 -
            }
716 +
            emit::emit(s.e,
717 +
                encode::remuw(rd, rs1, rs2)
718 +
                    if typ == il::Type::W32 else
719 +
                encode::remu(rd, rs1, rs2));
726 720
        }
727 721
        case il::BinOp::And =>
728 722
            selectBinOp(s, rd, rs1, b, BinOp::And, super::SCRATCH2),
729 723
        case il::BinOp::Or =>
730 724
            selectBinOp(s, rd, rs1, b, BinOp::Or, super::SCRATCH2),
lib/std/arch/rv64/printer.rad +1 -4
22 22
    "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
23 23
];
24 24
25 25
/// Get register name from number.
26 26
fn regName(n: u8) -> *[u8] {
27 -
    if n >= 32 {
28 -
        return "?";
29 -
    }
30 -
    return REG_NAMES[n as u32];
27 +
    return "?" if n >= 32 else REG_NAMES[n as u32];
31 28
}
32 29
33 30
/// Get register name from Reg.
34 31
fn regNameR(r: super::Reg) -> *[u8] {
35 32
    return regName(r.n);
lib/std/fmt.rad +4 -16
39 39
40 40
/// Format a i32 by writing it to the provided buffer.
41 41
pub fn formatI32(val: i32, buffer: *mut [u8]) -> *[u8] {
42 42
    debug::assert(buffer.len >= I32_STR_LEN);
43 43
44 -
    let mut x: u32 = 0;
45 -
    let mut i: u32 = buffer.len;
46 44
    let neg: bool = val < 0;
47 -
48 -
    if neg {
49 -
        x = -val as u32;
50 -
    } else {
51 -
        x = val as u32;
52 -
    }
45 +
    let mut x: u32 = -val as u32 if neg else val as u32;
46 +
    let mut i: u32 = buffer.len;
53 47
    // Handle the zero case separately to ensure a single '0' is written.
54 48
    if x == 0 {
55 49
        i -= 1;
56 50
        buffer[i] = '0';
57 51
    } else {
92 86
93 87
/// Format a i64 by writing it to the provided buffer.
94 88
pub fn formatI64(val: i64, buffer: *mut [u8]) -> *[u8] {
95 89
    debug::assert(buffer.len >= I64_STR_LEN);
96 90
97 -
    let mut x: u64 = 0;
98 -
    let mut i: u32 = buffer.len;
99 91
    let neg: bool = val < 0;
100 -
101 -
    if neg {
102 -
        x = -val as u64;
103 -
    } else {
104 -
        x = val as u64;
105 -
    }
92 +
    let mut x: u64 = -val as u64 if neg else val as u64;
93 +
    let mut i: u32 = buffer.len;
106 94
    if x == 0 {
107 95
        i -= 1;
108 96
        buffer[i] = '0';
109 97
    } else {
110 98
        while x != 0 {
lib/std/lang/ast/printer.rad +9 -18
75 75
        case super::TypeSig::Opaque => return sexpr::sym("opaque"),
76 76
        case super::TypeSig::Bool => return sexpr::sym("bool"),
77 77
        case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)),
78 78
        case super::TypeSig::Array { itemType, length } =>
79 79
            return sexpr::list(a, "array", &[toExpr(a, itemType), toExpr(a, length)]),
80 -
        case super::TypeSig::Slice { itemType, mutable } => {
81 -
            if mutable {
82 -
                return sexpr::list(a, "slice", &[sexpr::sym("mut"), toExpr(a, itemType)]);
83 -
            }
84 -
            return sexpr::list(a, "slice", &[toExpr(a, itemType)]);
85 -
        }
86 -
        case super::TypeSig::Pointer { valueType, mutable } => {
87 -
            if mutable {
88 -
                return sexpr::list(a, "ptr", &[sexpr::sym("mut"), toExpr(a, valueType)]);
89 -
            }
90 -
            return sexpr::list(a, "ptr", &[toExpr(a, valueType)]);
91 -
        }
80 +
        case super::TypeSig::Slice { itemType, mutable } =>
81 +
            return sexpr::list(a, "slice", &[sexpr::sym("mut"), toExpr(a, itemType)]) if mutable
82 +
                else sexpr::list(a, "slice", &[toExpr(a, itemType)]),
83 +
        case super::TypeSig::Pointer { valueType, mutable } =>
84 +
            return sexpr::list(a, "ptr", &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable
85 +
                else sexpr::list(a, "ptr", &[toExpr(a, valueType)]),
92 86
        case super::TypeSig::Optional { valueType } =>
93 87
            return sexpr::list(a, "?", &[toExpr(a, valueType)]),
94 88
        case super::TypeSig::Nominal(name) => return toExpr(a, name),
95 89
        case super::TypeSig::Record { fields, .. } =>
96 90
            return sexpr::list(a, "record", nodeListToExprs(a, &fields)),
273 267
            return sexpr::list(a, "[]", &[toExpr(a, container), toExpr(a, index)]),
274 268
        case super::NodeValue::FieldAccess(acc) =>
275 269
            return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
276 270
        case super::NodeValue::ScopeAccess(acc) =>
277 271
            return sexpr::list(a, "::", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
278 -
        case super::NodeValue::AddressOf(addr) => {
279 -
            if addr.mutable {
280 -
                return sexpr::list(a, "&mut", &[toExpr(a, addr.target)]);
281 -
            }
282 -
            return sexpr::list(a, "&", &[toExpr(a, addr.target)]);
283 -
        }
272 +
        case super::NodeValue::AddressOf(addr) =>
273 +
            return sexpr::list(a, "&mut", &[toExpr(a, addr.target)]) if addr.mutable
274 +
                else sexpr::list(a, "&", &[toExpr(a, addr.target)]),
284 275
        case super::NodeValue::Deref(target) =>
285 276
            return sexpr::list(a, "deref", &[toExpr(a, target)]),
286 277
        case super::NodeValue::As(cast) =>
287 278
            return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]),
288 279
        case super::NodeValue::ArrayLit(elems) =>
lib/std/lang/gen/bitset.rad +2 -8
136 136
    let numWordsA = wordsFor(a.len);
137 137
    let numWordsB = wordsFor(b.len);
138 138
    let maxWords = max(numWordsA, numWordsB);
139 139
140 140
    for i in 0..maxWords {
141 -
        let mut wordA: u32 = 0;
142 -
        let mut wordB: u32 = 0;
143 -
        if i < numWordsA {
144 -
            wordA = a.bits[i];
145 -
        }
146 -
        if i < numWordsB {
147 -
            wordB = b.bits[i];
148 -
        }
141 +
        let wordA = a.bits[i] if i < numWordsA else 0;
142 +
        let wordB = b.bits[i] if i < numWordsB else 0;
149 143
        if wordA != wordB {
150 144
            return false;
151 145
        }
152 146
    }
153 147
    return true;
lib/std/lang/gen/regalloc/spill.rad +2 -8
178 178
fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) {
179 179
    for b in 0..func.blocks.len {
180 180
        let block = &func.blocks[b];
181 181
182 182
        // Exponential weight for loop depth, capped to avoid overflow.
183 -
        let mut depth = block.loopDepth;
184 -
        if depth > MAX_LOOP_WEIGHT {
185 -
            depth = MAX_LOOP_WEIGHT;
186 -
        }
183 +
        let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth;
187 184
        let weight: u32 = 1 << depth;
188 185
189 186
        // Count block parameter definitions.
190 187
        for p in block.params {
191 188
            if p.value.n < costs.len {
225 222
            c.entries[j] = c.entries[j - 1];
226 223
            j -= 1;
227 224
        }
228 225
        c.entries[j] = key;
229 226
    }
230 -
    let mut toSpill = excess;
231 -
    if toSpill > c.n {
232 -
        toSpill = c.n;
233 -
    }
227 +
    let toSpill = c.n if excess > c.n else excess;
234 228
    for i in 0..toSpill {
235 229
        bitset::set(spilled, c.entries[i].reg);
236 230
        bitset::clear(source, c.entries[i].reg);
237 231
    }
238 232
}
lib/std/lang/lower.rad +31 -98
1072 1072
// Functions for building the data section from const/static
1073 1073
// declarations and inline literals.
1074 1074
1075 1075
/// Convert a constant integer payload to a signed 64-bit value.
1076 1076
fn constIntToI64(intVal: resolver::ConstInt) -> i64 {
1077 -
    let mut value = intVal.magnitude as i64;
1078 -
    if intVal.negative {
1079 -
        value = 0 - value;
1080 -
    }
1081 -
    return value;
1077 +
    return (0 - intVal.magnitude as i64) if intVal.negative else intVal.magnitude as i64;
1082 1078
}
1083 1079
1084 1080
/// Convert a scalar constant value to an i64.
1085 1081
/// String constants are not handled here and should be checked before calling.
1086 1082
/// Panics if a non-scalar value is passed.
1087 1083
fn constToScalar(val: resolver::ConstValue) -> i64 {
1088 1084
    match val {
1089 -
        case resolver::ConstValue::Bool(b) => {
1090 -
            if b {
1091 -
                return 1;
1092 -
            } else {
1093 -
                return 0;
1094 -
            }
1095 -
        }
1085 +
        case resolver::ConstValue::Bool(b) => return 1 if b else 0,
1096 1086
        case resolver::ConstValue::Char(c) => return c as i64,
1097 1087
        case resolver::ConstValue::Int(i) => return constIntToI64(i),
1098 1088
        else => panic,
1099 1089
    }
1100 1090
}
1399 1389
        let fieldInfo = recInfo.fields[i];
1400 1390
        let fieldOffset = fieldInfo.offset as u32;
1401 1391
1402 1392
        // Slot extends to the next field's offset,
1403 1393
        // or record size for the last field.
1404 -
        let mut slotEnd = layout.size;
1405 -
        if i + 1 < recInfo.fieldsLen {
1406 -
            slotEnd = recInfo.fields[i + 1].offset as u32;
1407 -
        }
1394 +
        let slotEnd = recInfo.fields[i + 1].offset as u32 if i + 1 < recInfo.fieldsLen else layout.size;
1408 1395
        let slotSize = slotEnd - fieldOffset;
1409 1396
1410 1397
        try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b);
1411 1398
    }
1412 1399
}
2876 2863
fn lowerParams(
2877 2864
    self: *mut FnLowerer,
2878 2865
    fnType: resolver::FnType,
2879 2866
    astParams: ast::NodeList
2880 2867
) -> *[il::Param] throws (LowerError) {
2881 -
    let mut offset: u32 = 0;
2882 -
    if self.returnReg != nil {
2883 -
        offset = 1;
2884 -
    }
2868 +
    let offset: u32 = 1 if self.returnReg != nil else 0;
2885 2869
    let totalLen = fnType.paramTypesLen + offset;
2886 2870
2887 2871
    if totalLen == 0 {
2888 2872
        return &[];
2889 2873
    }
2997 2981
    let optTy = resolver::typeFor(self.low.resolver, opt) else {
2998 2982
        throw LowerError::MissingType(opt);
2999 2983
    };
3000 2984
    // Handle `nil == nil` or `nil != nil`.
3001 2985
    if optTy == resolver::Type::Nil {
3002 -
        if isEq {
3003 -
            return il::Val::Imm(1);
3004 -
        } else {
3005 -
            return il::Val::Imm(0);
3006 -
        }
2986 +
        return il::Val::Imm(1) if isEq else il::Val::Imm(0);
3007 2987
    }
3008 2988
    let val = try lowerExpr(self, opt);
3009 2989
    let cmpReg = try optionalNilReg(self, val, optTy);
3010 2990
3011 2991
    // Null-pointer-optimized types compare a 64-bit pointer against zero.
3012 2992
    // Aggregate optionals compare an 8-bit tag byte against zero.
3013 -
    let mut cmpType = il::Type::W8;
3014 -
    if resolver::isOptionalPointer(optTy) {
3015 -
        cmpType = il::Type::W64;
3016 -
    }
2993 +
    let cmpType = il::Type::W64 if resolver::isOptionalPointer(optTy) else il::Type::W8;
3017 2994
3018 -
    if isEq {
3019 -
        return emitTypedBinOp(self, il::BinOp::Eq, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0));
3020 -
    } else {
3021 -
        return emitTypedBinOp(self, il::BinOp::Ne, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0));
3022 -
    }
2995 +
    let op = il::BinOp::Eq if isEq else il::BinOp::Ne;
2996 +
    return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0));
3023 2997
}
3024 2998
3025 2999
/// Load the payload value from a tagged value aggregate at the given offset.
3026 3000
fn tvalPayloadVal(self: *mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val {
3027 3001
    if payload == resolver::Type::Void {
3959 3933
/// Emit a tag comparison for void variant equality/inequality.
3960 3934
fn emitTagCmp(self: *mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val
3961 3935
    throws (LowerError)
3962 3936
{
3963 3937
    let reg = try emitValToReg(self, val);
3964 -
    let mut tag: il::Val = undefined;
3965 3938
3966 3939
    // For all-void unions, the value *is* the tag, not a pointer.
3940 +
    let mut tag: il::Val = undefined;
3967 3941
    if resolver::isVoidUnion(valType) {
3968 3942
        tag = il::Val::Reg(reg);
3969 3943
    } else {
3970 3944
        tag = loadTag(self, reg, TVAL_TAG_OFFSET, il::Type::W8);
3971 3945
    }
3972 -
    if op == ast::BinaryOp::Eq {
3973 -
        return emitTypedBinOp(self, il::BinOp::Eq, il::Type::W8, tag, il::Val::Imm(tagIdx));
3974 -
    }
3975 -
    return emitTypedBinOp(self, il::BinOp::Ne, il::Type::W8, tag, il::Val::Imm(tagIdx));
3946 +
    let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne;
3947 +
    return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx));
3976 3948
}
3977 3949
3978 3950
/// Logical "and" between two values. Returns the result in a register.
3979 3951
fn emitLogicalAnd(self: *mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val {
3980 3952
    let prev = left else {
5078 5050
5079 5051
    // Emit condition check.
5080 5052
    match *iter {
5081 5053
        case ForIter::Range { valVar, endVal, valType, unsigned, .. } => {
5082 5054
            let curVal = try useVar(self, valVar);
5083 -
            let mut cmp = il::CmpOp::Slt;
5084 -
            if unsigned {
5085 -
                cmp = il::CmpOp::Ult;
5086 -
            }
5055 +
            let cmp = il::CmpOp::Ult if unsigned else il::CmpOp::Slt;
5087 5056
            try emitBrCmp(self, cmp, valType, curVal, endVal, bodyBlock, endBlock);
5088 5057
        }
5089 5058
        case ForIter::Collection { idxVar, lengthVal, .. } => {
5090 5059
            let curIdx = try useVar(self, idxVar);
5091 5060
            try emitBrCmp(self, il::CmpOp::Slt, il::Type::W32, curIdx, lengthVal, bodyBlock, endBlock);
5426 5395
fn cmpOpFrom(op: ast::BinaryOp, unsigned: bool) -> ?il::CmpOp {
5427 5396
    match op {
5428 5397
        case ast::BinaryOp::Eq => return il::CmpOp::Eq,
5429 5398
        case ast::BinaryOp::Ne => return il::CmpOp::Ne,
5430 5399
        case ast::BinaryOp::Lt, ast::BinaryOp::Gt,
5431 -
             ast::BinaryOp::Gte, ast::BinaryOp::Lte => {
5432 -
            if unsigned {
5433 -
                return il::CmpOp::Ult;
5434 -
            } else {
5435 -
                return il::CmpOp::Slt;
5436 -
            }
5437 -
        }
5400 +
             ast::BinaryOp::Gte, ast::BinaryOp::Lte =>
5401 +
            return il::CmpOp::Ult if unsigned else il::CmpOp::Slt,
5438 5402
        else => return nil,
5439 5403
    }
5440 5404
}
5441 5405
5442 5406
/// Lower a binary operation.
5541 5505
        case ast::BinaryOp::Mul => {
5542 5506
            emit(self, il::Instr::BinOp { op: il::BinOp::Mul, typ, dst, a, b });
5543 5507
            needsExt = true;
5544 5508
        }
5545 5509
        case ast::BinaryOp::Div => {
5546 -
            if unsigned {
5547 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Udiv, typ, dst, a, b });
5548 -
            } else {
5549 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Sdiv, typ, dst, a, b });
5550 -
            }
5510 +
            let op = il::BinOp::Udiv if unsigned else il::BinOp::Sdiv;
5511 +
            emit(self, il::Instr::BinOp { op, typ, dst, a, b });
5551 5512
            needsExt = true;
5552 5513
        }
5553 5514
        case ast::BinaryOp::Mod => {
5554 -
            if unsigned {
5555 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Urem, typ, dst, a, b });
5556 -
            } else {
5557 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Srem, typ, dst, a, b });
5558 -
            }
5515 +
            let op = il::BinOp::Urem if unsigned else il::BinOp::Srem;
5516 +
            emit(self, il::Instr::BinOp { op, typ, dst, a, b });
5559 5517
            needsExt = true;
5560 5518
        }
5561 5519
        case ast::BinaryOp::BitAnd => emit(self, il::Instr::BinOp { op: il::BinOp::And, typ, dst, a, b }),
5562 5520
        case ast::BinaryOp::BitOr => emit(self, il::Instr::BinOp { op: il::BinOp::Or, typ, dst, a, b }),
5563 5521
        case ast::BinaryOp::BitXor => emit(self, il::Instr::BinOp { op: il::BinOp::Xor, typ, dst, a, b }),
5564 5522
        case ast::BinaryOp::Shl => {
5565 5523
            emit(self, il::Instr::BinOp { op: il::BinOp::Shl, typ, dst, a, b });
5566 5524
            needsExt = true;
5567 5525
        }
5568 5526
        case ast::BinaryOp::Shr => {
5569 -
            if unsigned {
5570 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Ushr, typ, dst, a, b });
5571 -
            } else {
5572 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Sshr, typ, dst, a, b });
5573 -
            }
5527 +
            let op = il::BinOp::Ushr if unsigned else il::BinOp::Sshr;
5528 +
            emit(self, il::Instr::BinOp { op, typ, dst, a, b });
5574 5529
        }
5575 5530
        case ast::BinaryOp::Eq => emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ, dst, a, b }),
5576 5531
        case ast::BinaryOp::Ne => emit(self, il::Instr::BinOp { op: il::BinOp::Ne, typ, dst, a, b }),
5577 5532
        case ast::BinaryOp::Lt => {
5578 -
            if unsigned {
5579 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Ult, typ, dst, a, b });
5580 -
            } else {
5581 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Slt, typ, dst, a, b });
5582 -
            }
5533 +
            let op = il::BinOp::Ult if unsigned else il::BinOp::Slt;
5534 +
            emit(self, il::Instr::BinOp { op, typ, dst, a, b });
5583 5535
        }
5584 5536
        case ast::BinaryOp::Gt => { // `a > b` = `b < a`
5585 -
            if unsigned {
5586 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Ult, typ, dst, a: b, b: a });
5587 -
            } else {
5588 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Slt, typ, dst, a: b, b: a });
5589 -
            }
5537 +
            let op = il::BinOp::Ult if unsigned else il::BinOp::Slt;
5538 +
            emit(self, il::Instr::BinOp { op, typ, dst, a: b, b: a });
5590 5539
        }
5591 5540
        case ast::BinaryOp::Lte => { // `a <= b` = `b >= a`
5592 -
            if unsigned {
5593 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Uge, typ, dst, a: b, b: a });
5594 -
            } else {
5595 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Sge, typ, dst, a: b, b: a });
5596 -
            }
5541 +
            let op = il::BinOp::Uge if unsigned else il::BinOp::Sge;
5542 +
            emit(self, il::Instr::BinOp { op, typ, dst, a: b, b: a });
5597 5543
        }
5598 5544
        case ast::BinaryOp::Gte => {
5599 -
            if unsigned {
5600 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Uge, typ, dst, a, b });
5601 -
            } else {
5602 -
                emit(self, il::Instr::BinOp { op: il::BinOp::Sge, typ, dst, a, b });
5603 -
            }
5545 +
            let op = il::BinOp::Uge if unsigned else il::BinOp::Sge;
5546 +
            emit(self, il::Instr::BinOp { op, typ, dst, a, b });
5604 5547
        }
5605 5548
        // Logical xor on booleans is equivalent to not equal.
5606 5549
        case ast::BinaryOp::Xor => emit(self, il::Instr::BinOp { op: il::BinOp::Ne, typ, dst, a, b }),
5607 5550
        // Short-circuit ops are handled elsewhere.
5608 5551
        case ast::BinaryOp::And, ast::BinaryOp::Or => panic,
6081 6024
    let needsRetBuf = isThrowing or (isAggregateType(retTy) and not isSmallAggregate(retTy));
6082 6025
6083 6026
    // Lower function value and arguments, reserving an extra slot for the
6084 6027
    // hidden return buffer when needed.
6085 6028
    let callee = try lowerCallee(self, call.callee);
6086 -
    let mut offset: u32 = 0;
6087 -
    if needsRetBuf {
6088 -
        offset = 1;
6089 -
    }
6029 +
    let offset: u32 = 1 if needsRetBuf else 0;
6090 6030
    let args = try allocVals(self, call.args.len + offset);
6091 6031
    for i in 0..call.args.len {
6092 6032
        args[i + offset] = try lowerExpr(self, call.args.list[i]);
6093 6033
    }
6094 6034
6336 6276
        }
6337 6277
        case ast::NodeValue::ScopeAccess(_) => {
6338 6278
            val = try lowerScopeAccess(self, node);
6339 6279
        }
6340 6280
        case ast::NodeValue::Number(lit) => {
6341 -
            let mut mag = lit.magnitude as i64;
6342 -
            if lit.negative {
6343 -
                mag = -mag;
6344 -
            }
6281 +
            let mag = -(lit.magnitude as i64) if lit.negative else lit.magnitude as i64;
6345 6282
            val = il::Val::Imm(mag);
6346 6283
        }
6347 6284
        case ast::NodeValue::Bool(b) => {
6348 -
            if b {
6349 -
                val = il::Val::Imm(1);
6350 -
            } else {
6351 -
                val = il::Val::Imm(0);
6352 -
            }
6285 +
            val = il::Val::Imm(1) if b else il::Val::Imm(0);
6353 6286
        }
6354 6287
        case ast::NodeValue::Char(c) => {
6355 6288
            val = il::Val::Imm(c as i64);
6356 6289
        }
6357 6290
        case ast::NodeValue::Nil => {
lib/std/lang/parser.rad +2 -8
1601 1601
    p: *mut Parser,
1602 1602
    mode: RecordFieldMode
1603 1603
) -> ast::NodeList
1604 1604
    throws (ParseError)
1605 1605
{
1606 -
    let mut terminator: scanner::TokenKind = undefined;
1606 +
    let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled
1607 +
        else scanner::TokenKind::RParen;
1607 1608
    let mut fields = ast::nodeList(p.arena, MAX_RECORD_FIELDS);
1608 -
1609 -
    match mode {
1610 -
        case RecordFieldMode::Labeled =>
1611 -
            terminator = scanner::TokenKind::RBrace,
1612 -
        case RecordFieldMode::Unlabeled =>
1613 -
            terminator = scanner::TokenKind::RParen,
1614 -
    }
1615 1609
    while not check(p, terminator) {
1616 1610
        let mut recordField: ast::NodeValue = undefined;
1617 1611
        match mode {
1618 1612
            case RecordFieldMode::Labeled => {
1619 1613
                // Allow optional `let` keyword before field name.
lib/std/lang/resolver.rad +6 -32
633 633
}
634 634
635 635
/// Unwrap a pointer type for pattern matching.
636 636
pub fn unwrapMatchSubject(ty: Type) -> MatchSubject {
637 637
    if let case Type::Pointer { target, mutable } = ty {
638 -
        let mut by = MatchBy::Ref;
639 -
        if mutable {
640 -
            by = MatchBy::MutRef;
641 -
        }
638 +
        let by = MatchBy::MutRef if mutable else MatchBy::Ref;
642 639
        return MatchSubject { effectiveTy: *target, by };
643 640
    }
644 641
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
645 642
}
646 643
5298 5295
        }
5299 5296
        case ast::TypeSig::Bool => {
5300 5297
            return Type::Bool;
5301 5298
        }
5302 5299
        case ast::TypeSig::Integer { width, sign } => {
5300 +
            let u = sign == ast::Signedness::Unsigned;
5303 5301
            match width {
5304 -
                case 1 => {
5305 -
                    if sign == ast::Signedness::Unsigned {
5306 -
                        return Type::U8;
5307 -
                    } else {
5308 -
                        return Type::I8;
5309 -
                    }
5310 -
                }
5311 -
                case 2 => {
5312 -
                    if sign == ast::Signedness::Unsigned {
5313 -
                        return Type::U16;
5314 -
                    } else {
5315 -
                        return Type::I16;
5316 -
                    }
5317 -
                }
5318 -
                case 4 => {
5319 -
                    if sign == ast::Signedness::Unsigned {
5320 -
                        return Type::U32;
5321 -
                    } else {
5322 -
                        return Type::I32;
5323 -
                    }
5324 -
                }
5325 -
                case 8 => {
5326 -
                    if sign == ast::Signedness::Unsigned {
5327 -
                        return Type::U64;
5328 -
                    } else {
5329 -
                        return Type::I64;
5330 -
                    }
5331 -
                }
5302 +
                case 1 => return Type::U8 if u else Type::I8,
5303 +
                case 2 => return Type::U16 if u else Type::I16,
5304 +
                case 4 => return Type::U32 if u else Type::I32,
5305 +
                case 8 => return Type::U64 if u else Type::I64,
5332 5306
                else => {
5333 5307
                    panic "resolveTypeSig: invalid integer width";
5334 5308
                }
5335 5309
            }
5336 5310
        }
lib/std/lang/resolver/printer.rad +1 -5
381 381
        case super::ErrorKind::OptionalMatchMissingNil => {
382 382
            io::print("optional match non-exhaustive: missing nil case");
383 383
        }
384 384
        case super::ErrorKind::BoolMatchMissing(val) => {
385 385
            io::print("bool match non-exhaustive: missing case for `");
386 -
            if val {
387 -
                io::print("true");
388 -
            } else {
389 -
                io::print("false");
390 -
            }
386 +
            io::print("true" if val else "false");
391 387
            io::print("`");
392 388
        }
393 389
        case super::ErrorKind::MatchNonExhaustive => {
394 390
            io::print("match non-exhaustive: requires `else` or binding catch-all");
395 391
        }
lib/std/lang/sexpr.rad +1 -4
98 98
pub fn write(out: *mut Output, s: *[u8]) {
99 99
    match *out {
100 100
        case Output::Stdout => io::print(s),
101 101
        case Output::Buffer { buf, pos } => {
102 102
            let remaining = buf.len - *pos;
103 -
            let mut toWrite = s.len;
104 -
            if toWrite > remaining {
105 -
                toWrite = remaining;
106 -
            }
103 +
            let toWrite = remaining if s.len > remaining else s.len;
107 104
            for i in 0..toWrite {
108 105
                buf[*pos] = s[i];
109 106
                *pos += 1;
110 107
            }
111 108
        }
lib/std/mem.rad +1 -4
73 73
/// Returns `-1` when `a < b`, `1` when `a > b`, and `0` when equal.
74 74
pub fn cmp(a: *[u8], b: *[u8]) -> i32 {
75 75
    let aLen = a.len;
76 76
    let bLen = b.len;
77 77
78 -
    let mut common = aLen;
79 -
    if bLen < common {
80 -
        common = bLen;
81 -
    }
78 +
    let common = bLen if bLen < aLen else aLen;
82 79
    for i in 0..common {
83 80
        let aByte = a[i];
84 81
        let bByte = b[i];
85 82
        if aByte < bByte {
86 83
            return -1;