lang: Lower linear references

a8787514f6fd1e016bf3bb59c64dd718d324a9d43747de93529bf07d4fa36f30
Alexis Sellier committed ago 1 parent 7e3db9f1
lib/std/lang/lower.rad +72 -47
3255 3255
            set payload = tvalPayloadAddr(self, base, valOffset),
3256 3256
    };
3257 3257
    return newVar(self, name, ilType(self.low, bindType), mutable, payload);
3258 3258
}
3259 3259
3260 +
/// Bind an identifier from a matched subject.
3260 3261
fn bindMatchVariable(
3261 3262
    self: *mut FnLowerer,
3262 3263
    subject: *MatchSubject,
3263 3264
    binding: *ast::Node,
3264 3265
    mutable: bool
3356 3357
        };
3357 3358
        try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock);
3358 3359
    }
3359 3360
}
3360 3361
3362 +
/// Bind fields from a record pattern.
3361 3363
fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3362 3364
    // No fields to bind (e.g., `{ .. }`).
3363 3365
    if lit.fields.len == 0 {
3364 3366
        return;
3365 3367
    }
3855 3857
}
3856 3858
3857 3859
/// Lower a `let-else` statement.
3858 3860
fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) {
3859 3861
    let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee);
3860 -
    let mut mergeBlock: BlockId = undefined;
3862 +
    let mut successBlock: BlockId = undefined;
3861 3863
    if letElse.pattern.guard == nil {
3862 -
        set mergeBlock = try createBlock(self, "merge");
3864 +
        set successBlock = try createBlock(self, "success");
3863 3865
    }
3864 -
    // Else branch executes when the pattern fails to match.
3865 3866
    let elseBlock = try createBlock(self, "else");
3866 3867
3867 -
    // Evaluate pattern and jump to @end or @else.
3868 -
    try lowerPatternMatch(self, &subject, &letElse.pattern, &mut mergeBlock, "merge", elseBlock);
3868 +
    try lowerPatternMatch(
3869 +
        self,
3870 +
        &subject,
3871 +
        &letElse.pattern,
3872 +
        &mut successBlock,
3873 +
        "success",
3874 +
        elseBlock,
3875 +
    );
3876 +
    let mut bindingVar: ?Var = nil;
3877 +
    if let case ast::PatternKind::Binding = letElse.pattern.kind {
3878 +
        set bindingVar = lookupLocalVar(self, letElse.pattern.pattern);
3879 +
    }
3880 +
    let mergeBlock = try createBlock(self, "merge");
3881 +
    try emitJmp(self, mergeBlock);
3882 +
3869 3883
    try switchToAndSeal(self, elseBlock);
3870 -
    try lowerNode(self, letElse.elseBranch);
3884 +
    if try typeOf(self, letElse.elseBranch) == resolver::Type::Never {
3885 +
        try lowerNode(self, letElse.elseBranch);
3886 +
    } else {
3887 +
        let fallback = try lowerExpr(self, letElse.elseBranch);
3888 +
        if let variable = bindingVar {
3889 +
            defVar(self, variable, fallback);
3890 +
        }
3891 +
        try emitJmp(self, mergeBlock);
3892 +
    }
3871 3893
3872 -
    // Continue in @merge. The else branch must diverge, so @merge has only
3873 -
    // one predecessor.
3874 3894
    try switchToAndSeal(self, mergeBlock);
3875 3895
}
3876 3896
3877 3897
/// Lower a `while let` loop as a match-driven loop.
3878 3898
fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) {
4050 4070
}
4051 4071
4052 4072
/// Check if a resolver type lowers to an aggregate in memory.
4053 4073
fn isAggregateType(typ: resolver::Type) -> bool {
4054 4074
    match typ {
4055 -
        case resolver::Type::Nominal(_) => {
4056 -
            // Void unions are small enough to pass by value.
4057 -
            return not resolver::isVoidUnion(typ);
4058 -
        }
4059 4075
        case resolver::Type::Slice { .. },
4060 -
             resolver::Type::TraitObject { .. },
4061 -
             resolver::Type::Array(_),
4062 -
             resolver::Type::Nil => return true,
4076 +
             resolver::Type::TraitObject { .. } => return true,
4063 4077
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4064 4078
            // Optional pointers are scalar due to NPO.
4065 4079
            return false;
4066 4080
        }
4067 4081
        case resolver::Type::Optional(_) => {
4068 4082
            // All other optionals, including optional slices, are aggregates.
4069 4083
            return true;
4070 4084
        }
4085 +
        case resolver::Type::Nominal(_) => {
4086 +
            // Void unions are small enough to pass by value.
4087 +
            return not resolver::isVoidUnion(typ);
4088 +
        }
4089 +
        case resolver::Type::Array(_),
4090 +
             resolver::Type::Nil => return true,
4071 4091
        else => return false,
4072 4092
    }
4073 4093
}
4074 4094
4075 4095
/// Check if a resolver type is a small aggregate that can be
4217 4237
/// Build a `nil` value for an optional type.
4218 4238
///
4219 4239
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4220 4240
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4221 4241
fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4222 -
    match optType {
4223 -
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4224 -
            return il::Val::Imm(0);
4225 -
        }
4226 -
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => {
4227 -
            return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4228 -
        }
4229 -
        case resolver::Type::Optional(inner) => {
4230 -
            let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4231 -
            return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4232 -
        }
4233 -
        else => throw LowerError::ExpectedOptional,
4242 +
    let case resolver::Type::Optional(inner) = optType
4243 +
        else throw LowerError::ExpectedOptional;
4244 +
    if let case resolver::Type::Pointer { .. } = *inner {
4245 +
        return il::Val::Imm(0);
4246 +
    }
4247 +
    if let case resolver::Type::Slice { item, mutable, .. } = *inner {
4248 +
        return try buildSliceValue(
4249 +
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
4250 +
        );
4234 4251
    }
4252 +
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4253 +
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4235 4254
}
4236 4255
4237 4256
/// Build a result value for throwing functions.
4238 4257
fn buildResult(
4239 4258
    self: *mut FnLowerer,
4256 4275
    ptrVal: il::Val,
4257 4276
    lenVal: il::Val,
4258 4277
    capVal: il::Val
4259 4278
) -> il::Val throws (LowerError) {
4260 4279
    let sliceType = resolver::Type::Slice {
4261 -
        class: types::PointerClass::Owned, item: elemTy, mutable,
4280 +
        class: types::PointerClass::Unsafe,
4281 +
        item: elemTy,
4282 +
        mutable,
4262 4283
    };
4263 4284
    let dst = try emitReserve(self, sliceType);
4264 4285
    let ptrTy = resolver::Type::Pointer {
4265 -
        class: types::PointerClass::Owned, target: elemTy, mutable,
4286 +
        class: types::PointerClass::Unsafe,
4287 +
        target: elemTy,
4288 +
        mutable,
4266 4289
    };
4267 4290
4268 4291
    try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal);
4269 4292
    try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal);
4270 4293
    try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal);
4450 4473
    a: il::Reg,
4451 4474
    b: il::Reg,
4452 4475
    offset: i32
4453 4476
) -> il::Val throws (LowerError) {
4454 4477
    let ptrTy = resolver::Type::Pointer {
4455 -
        class: types::PointerClass::Owned, target: elemTy, mutable,
4478 +
        class: types::PointerClass::Unsafe,
4479 +
        target: elemTy,
4480 +
        mutable,
4456 4481
    };
4457 4482
    let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy);
4458 4483
    let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32);
4459 4484
4460 4485
    return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq);
4700 4725
    a: il::Reg,
4701 4726
    b: il::Reg,
4702 4727
    offset: i32
4703 4728
) -> il::Val throws (LowerError) {
4704 4729
    match typ {
4705 -
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => {
4706 -
            // Optional slices use null pointer optimization.
4707 -
            return try lowerSliceEq(self, item, mutable, a, b, offset);
4708 -
        }
4730 +
        case resolver::Type::Slice { item, mutable, .. } =>
4731 +
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4709 4732
        case resolver::Type::Optional(inner) => {
4733 +
            if let case resolver::Type::Slice { item, mutable, .. } = *inner {
4734 +
                // Optional slices use null pointer optimization.
4735 +
                return try lowerSliceEq(self, item, mutable, a, b, offset);
4736 +
            }
4710 4737
            return try lowerOptionalEq(self, *inner, a, b, offset);
4711 4738
        }
4712 -
        case resolver::Type::Slice { item, mutable, .. } =>
4713 -
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4714 4739
        case resolver::Type::Array(arr) =>
4715 4740
            return try lowerArrayEq(self, arr, a, b, offset),
4716 4741
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) =>
4717 4742
            return try lowerRecordEq(self, recInfo, a, b, offset),
4718 4743
        case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) =>
5090 5115
            else => throw LowerError::UnexpectedNodeValue(arrayNode),
5091 5116
        }
5092 5117
        let result = dataBuilderFinish(&b);
5093 5118
        let alignment = resolver::getTypeLayout(*item).alignment;
5094 5119
        return try lowerConstDataAsSlice(
5095 -
            self, result.values, alignment, not mutable, item, mutable, length
5120 +
            self, result.values, alignment, not mutable,
5121 +
            item, mutable, length
5096 5122
        );
5097 5123
    }
5098 5124
    let data = try lowerExpr(self, arrayNode);
5099 5125
    let count = il::Val::Imm(length as i64);
5100 5126
    return try buildSliceValue(self, item, mutable, data, count, count);
5120 5146
        case resolver::Type::Slice { item, .. } => {
5121 5147
            set elemType = *item;
5122 5148
            let sliceLen = loadSliceLen(self, baseReg);
5123 5149
            // Runtime safety check: index must be strictly less than slice length.
5124 5150
            try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, sliceLen);
5125 -
5126 5151
            set dataReg = loadSlicePtr(self, baseReg);
5127 5152
        }
5128 5153
        case resolver::Type::Array(arrInfo) => {
5129 5154
            set elemType = *arrInfo.item;
5130 5155
            // Runtime safety check: index must be strictly less than array length.
6147 6172
/// String literals are stored as global data and the result is a slice
6148 6173
/// pointing to the data with the appropriate length.
6149 6174
fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) {
6150 6175
    // Get the slice type from the node.
6151 6176
    let sliceTy = try typeOf(self, node);
6152 -
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
6153 -
        throw LowerError::ExpectedSliceOrArray;
6154 -
    };
6177 +
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6178 +
        else throw LowerError::ExpectedSliceOrArray;
6155 6179
    // Build the string data value.
6156 6180
    let ptr = try! alloc::alloc(
6157 6181
        self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6158 6182
    ) as *mut il::DataValue;
6159 6183
6160 6184
    set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 };
6161 6185
6162 -
    return try lowerConstDataAsSlice(self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len);
6186 +
    return try lowerConstDataAsSlice(
6187 +
        self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len
6188 +
    );
6163 6189
}
6164 6190
6165 6191
/// Lower a builtin call expression.
6166 6192
fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6167 6193
    match kind {
6179 6205
fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6180 6206
    if args.len <> 2 and args.len <> 3 {
6181 6207
        throw LowerError::InvalidArgCount;
6182 6208
    }
6183 6209
    let sliceTy = try typeOf(self, node);
6184 -
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
6185 -
        throw LowerError::ExpectedSliceOrArray;
6186 -
    };
6210 +
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6211 +
        else throw LowerError::ExpectedSliceOrArray;
6187 6212
    let ptrVal = try lowerExpr(self, args[0]);
6188 6213
    let lenVal = try lowerExpr(self, args[1]);
6189 6214
    let mut capVal = lenVal;
6190 6215
    if args.len == 3 {
6191 6216
        set capVal = try lowerExpr(self, args[2]);
7287 7312
             resolver::Type::U32 => return il::Type::W32,
7288 7313
        case resolver::Type::I64,
7289 7314
             resolver::Type::U64,
7290 7315
             resolver::Type::Pointer { .. },
7291 7316
             resolver::Type::Slice { .. },
7317 +
             resolver::Type::TraitObject { .. },
7292 7318
             resolver::Type::Array(_),
7293 7319
             resolver::Type::Optional(_),
7294 -
             resolver::Type::Fn(_),
7295 -
             resolver::Type::TraitObject { .. } => return il::Type::W64,
7320 +
             resolver::Type::Fn(_) => return il::Type::W64,
7296 7321
        case resolver::Type::Nominal(_) => {
7297 7322
            if resolver::isVoidUnion(typ) {
7298 7323
                return il::Type::W8;
7299 7324
            }
7300 7325
            return il::Type::W64;