Handle scoped access constants in lowerer

9f542472d6d40fb6cb881aa6fd47a7e2922c0447fb269981cfefdb2a08ab793c
Alexis Sellier committed ago 1 parent 51330bdd
lib/std/lang/lower.rad +50 -22
1215 1215
        set irTyp = ilType(self, typ);
1216 1216
    }
1217 1217
    return il::DataItem::Val { typ: irTyp, val: constToScalar(val) };
1218 1218
}
1219 1219
1220 +
/// Lower scalar-like constant nodes into data values, including fallback handling
1221 +
/// for void-variant tags and slice string initializers.
1222 +
fn lowerConstScalarDataInto(
1223 +
    self: *mut Lowerer,
1224 +
    node: *ast::Node,
1225 +
    ty: resolver::Type,
1226 +
    dataPrefix: *[u8],
1227 +
    b: *mut DataValueBuilder
1228 +
) throws (LowerError) {
1229 +
    let val = resolver::constValueEntry(self.resolver, node) else {
1230 +
        if let idx = voidVariantIndex(self.resolver, node) {
1231 +
            dataBuilderPush(b, il::DataValue {
1232 +
                item: il::DataItem::Val { typ: il::Type::W8, val: idx },
1233 +
                count: 1
1234 +
            });
1235 +
            return;
1236 +
        }
1237 +
        throw LowerError::MissingConst(node);
1238 +
    };
1239 +
1240 +
    if let case resolver::ConstValue::String(s) = val {
1241 +
        if let case resolver::Type::Slice { .. } = ty {
1242 +
            let strSym = try getOrCreateStringData(self, s, dataPrefix);
1243 +
            dataSliceHeader(b, strSym, s.len);
1244 +
            return;
1245 +
        }
1246 +
    }
1247 +
    dataBuilderPush(b, il::DataValue {
1248 +
        item: constValueToDataItem(self, val, ty),
1249 +
        count: 1
1250 +
    });
1251 +
}
1252 +
1220 1253
/// Lower a constant or static declaration to the data section.
1221 1254
fn lowerDataDecl(
1222 1255
    self: *mut Lowerer,
1223 1256
    node: *ast::Node,
1224 1257
    value: *ast::Node,
1352 1385
                case resolver::SymbolData::Variant { .. } =>
1353 1386
                    try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b),
1354 1387
                case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => {
1355 1388
                    try lowerConstRecordCtorInto(self, call.args, recInfo, dataPrefix, b);
1356 1389
                }
1357 -
                else => throw LowerError::MissingConst(node),
1390 +
                else => throw LowerError::MissingConst(node)
1358 1391
            }
1359 1392
        }
1360 1393
        case ast::NodeValue::AddressOf(addr) => {
1361 1394
            try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b);
1362 1395
        }
1367 1400
            let case ast::NodeValue::ConstDecl(decl) = sym.node.value
1368 1401
                else throw LowerError::MissingConst(node);
1369 1402
1370 1403
            try lowerConstDataInto(self, decl.value, ty, slotSize, dataPrefix, b);
1371 1404
        },
1372 -
        else => {
1373 -
            // Scalar values: integers, bools, strings, void union variants, etc.
1374 -
            let val = resolver::constValueEntry(self.resolver, node)
1375 -
                else throw LowerError::MissingConst(node);
1376 -
1377 -
            if let case resolver::ConstValue::String(s) = val {
1378 -
                if let case resolver::Type::Slice { .. } = ty {
1379 -
                    let strSym = try getOrCreateStringData(self, s, dataPrefix);
1380 -
                    dataSliceHeader(b, strSym, s.len);
1381 -
                } else {
1382 -
                    dataBuilderPush(b, il::DataValue {
1383 -
                        item: constValueToDataItem(self, val, ty),
1384 -
                        count: 1
1385 -
                    });
1386 -
                }
1405 +
        case ast::NodeValue::ScopeAccess(_) => {
1406 +
            let sym = resolver::nodeData(self.resolver, node).sym
1407 +
                else throw LowerError::MissingSymbol(node);
1408 +
            if let case ast::NodeValue::ConstDecl(decl) = sym.node.value {
1409 +
                try lowerConstDataInto(self, decl.value, ty, slotSize, dataPrefix, b);
1387 1410
            } else {
1388 -
                dataBuilderPush(b, il::DataValue {
1389 -
                    item: constValueToDataItem(self, val, ty),
1390 -
                    count: 1
1391 -
                });
1411 +
                try lowerConstScalarDataInto(self, node, ty, dataPrefix, b);
1392 1412
            }
1393 1413
        }
1414 +
        else => {
1415 +
            // Scalar values: integers, bools, strings, void union variants, etc.
1416 +
            try lowerConstScalarDataInto(self, node, ty, dataPrefix, b);
1417 +
        }
1394 1418
    }
1395 1419
    // Pad to fill the slot.
1396 1420
    let padding = slotSize - layout.size;
1397 1421
    if padding > 0 {
1398 1422
        dataBuilderPush(b, il::DataValue { item: il::DataItem::Undef, count: padding });
6757 6781
    let sym = data.sym else {
6758 6782
        throw LowerError::MissingSymbol(node);
6759 6783
    };
6760 6784
    match sym.data {
6761 6785
        case resolver::SymbolData::Variant { index, .. } => {
6786 +
            let mut indexValue = index as i64;
6787 +
            if let idx = voidVariantIndex(self.low.resolver, node) {
6788 +
                set indexValue = idx;
6789 +
            }
6762 6790
            // Void union variant like `Option::None`.
6763 6791
            if data.ty == resolver::Type::Unknown {
6764 6792
                throw LowerError::MissingType(node);
6765 6793
            }
6766 6794
            // All-void unions are passed as scalars (the tag byte).
6767 6795
            // Return an immediate instead of building a tagged aggregate.
6768 6796
            if resolver::isVoidUnion(data.ty) {
6769 -
                return il::Val::Imm(index as i64);
6797 +
                return il::Val::Imm(indexValue);
6770 6798
            }
6771 6799
            let unionInfo = unionInfoFromType(data.ty) else {
6772 6800
                throw LowerError::MissingMetadata;
6773 6801
            };
6774 6802
            let valOffset = unionInfo.valOffset as i32;
6775 -
            return try buildTagged(self, resolver::getTypeLayout(data.ty), index as i64, nil, resolver::Type::Void, 1, valOffset);
6803 +
            return try buildTagged(self, resolver::getTypeLayout(data.ty), indexValue, nil, resolver::Type::Void, 1, valOffset);
6776 6804
        }
6777 6805
        case resolver::SymbolData::Constant { type, .. } => {
6778 6806
            // Constant without compile-time value (e.g. record constant);
6779 6807
            // load from data section.
6780 6808
            let src = emitDataAddr(self, sym);
test/tests/lower.const.record.ident.rad added +18 -0
1 +
record Reg(u8);
2 +
3 +
constant A0: Reg = Reg(10);
4 +
constant A1: Reg = Reg(11);
5 +
6 +
record Entry {
7 +
    name: *[u8],
8 +
    reg: Reg,
9 +
}
10 +
11 +
constant ENTRIES: [Entry; 2] = [
12 +
    { name: "a0", reg: A0 },
13 +
    { name: "a1", reg: A1 },
14 +
];
15 +
16 +
@default fn main() -> i32 {
17 +
    return ENTRIES.len as i32;
18 +
}
test/tests/lower.private.union.const.rad added +18 -0
1 +
union Kind {
2 +
    A,
3 +
    B,
4 +
}
5 +
6 +
record Entry {
7 +
    name: *[u8],
8 +
    kind: Kind,
9 +
}
10 +
11 +
constant ENTRIES: [Entry; 2] = [
12 +
    { name: "a", kind: Kind::A },
13 +
    { name: "b", kind: Kind::B },
14 +
];
15 +
16 +
@default fn main() -> i32 {
17 +
    return ENTRIES.len as i32;
18 +
}
test/tests/lower.record.scalar.record.const.rad added +15 -0
1 +
record Reg(u8);
2 +
3 +
record Entry {
4 +
    name: *[u8],
5 +
    reg: Reg,
6 +
}
7 +
8 +
constant ENTRIES: [Entry; 2] = [
9 +
    { name: "a0", reg: Reg(10) },
10 +
    { name: "a1", reg: Reg(11) },
11 +
];
12 +
13 +
@default fn main() -> i32 {
14 +
    return ENTRIES.len as i32;
15 +
}