Extract `computeUnionLayout` and `variantTag`

dd43ff042198173faf31c570a1dd532fe9d9789168a1e0438c7e7492cd3b60f3
Extract union layout computation and variant discriminant tag logic into
standalone functions. This is a pure refactoring with no behavior
change, preparing for reuse in generic union instantiation.
Alexis Sellier committed ago 1 parent 7c8cce71
lib/std/lang/resolver.rad +51 -32
154 154
    size: u32,
155 155
    /// Alignment in bytes.
156 156
    alignment: u32,
157 157
}
158 158
159 +
/// Computed union layout parameters.
160 +
record UnionLayoutInfo {
161 +
    layout: Layout,
162 +
    valOffset: u32,
163 +
    isAllVoid: bool,
164 +
}
165 +
159 166
/// Pre-computed metadata for slice range expressions.
160 167
/// Used by the lowerer.
161 168
pub record SliceRangeInfo {
162 169
    /// Element type of the resulting slice.
163 170
    itemType: *Type,
1381 1388
        size: PTR_SIZE + maxSize,
1382 1389
        alignment: max(PTR_SIZE, maxAlign),
1383 1390
    };
1384 1391
}
1385 1392
1393 +
/// Compute the layout for a union given its resolved variants.
1394 +
fn computeUnionLayout(variants: *[UnionVariant]) -> UnionLayoutInfo {
1395 +
    let tagSize: u32 = 1;
1396 +
    let mut maxVarSize: u32 = 0;
1397 +
    let mut maxVarAlign: u32 = 1;
1398 +
    let mut isAllVoid: bool = true;
1399 +
1400 +
    for i in 0..variants.len {
1401 +
        let payloadType = variants[i].valueType;
1402 +
        if payloadType != Type::Void {
1403 +
            isAllVoid = false;
1404 +
            let payloadLayout = getTypeLayout(payloadType);
1405 +
            maxVarSize = max(maxVarSize, payloadLayout.size);
1406 +
            maxVarAlign = max(maxVarAlign, payloadLayout.alignment);
1407 +
        }
1408 +
    }
1409 +
    let unionAlignment: u32 = max(1, maxVarAlign);
1410 +
    let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign);
1411 +
    let unionLayout = Layout {
1412 +
        size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment),
1413 +
        alignment: unionAlignment,
1414 +
    };
1415 +
    return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid };
1416 +
}
1417 +
1418 +
/// Compute the discriminant tag for a variant, advancing the iota counter.
1419 +
/// If the variant has an explicit `= N` value, uses that; otherwise uses iota.
1420 +
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: *mut u32) -> u32 {
1421 +
    let mut tag: u32 = *iota;
1422 +
    if let valueNode = variantDecl.value {
1423 +
        let case ast::NodeValue::Number(lit) = valueNode.value
1424 +
            else panic "variantTag: expected number literal";
1425 +
        tag = lit.magnitude as u32;
1426 +
    }
1427 +
    *iota = tag + 1;
1428 +
    return tag;
1429 +
}
1430 +
1386 1431
/// Check if a type is a union without payloads.
1387 1432
pub fn isVoidUnion(ty: Type) -> bool {
1388 1433
    let case Type::Nominal(info) = ty
1389 1434
        else return false;
1390 1435
    let case NominalType::Union(unionType) = *info
3139 3184
        if let ty = try visitOptional(self, variantDecl.type, Type::Unknown) {
3140 3185
            variantType = ty;
3141 3186
        }
3142 3187
        // Process the variant's explicit discriminant value if present.
3143 3188
        try visitOptional(self, variantDecl.value, variantType);
3144 -
        let mut variantTag: u32 = iota;
3145 -
        if let valueNode = variantDecl.value {
3146 -
            let case ast::NodeValue::Number(lit) = valueNode.value
3147 -
                else panic "resolveUnionBody: expected number literal for variant value";
3148 -
            variantTag = lit.magnitude as u32;
3149 -
        }
3150 -
        iota = variantTag + 1;
3189 +
        let tag = variantTag(variantDecl, &mut iota);
3151 3190
        // Create a symbol for this variant.
3152 -
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: variantTag };
3191 +
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
3153 3192
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
3154 3193
3155 3194
        variants[variantCount] = UnionVariant {
3156 3195
            name: variantName,
3157 3196
            valueType: variantType,
3158 3197
            symbol: variantSym,
3159 3198
        };
3160 3199
        variantCount += 1;
3161 3200
    }
3162 -
    // Compute cached layout values.
3163 -
    let tagSize: u32 = 1;
3164 -
    let mut maxVarSize: u32 = 0;
3165 -
    let mut maxVarAlign: u32 = 1;
3166 -
    let mut isAllVoid: bool = true;
3167 -
3168 -
    for i in 0..variantCount {
3169 -
        let payloadType = variants[i].valueType;
3170 -
        if payloadType != Type::Void {
3171 -
            isAllVoid = false;
3172 -
            let payloadLayout = getTypeLayout(payloadType);
3173 -
            maxVarSize = max(maxVarSize, payloadLayout.size);
3174 -
            maxVarAlign = max(maxVarAlign, payloadLayout.alignment);
3175 -
        }
3176 -
    }
3177 -
    let unionAlignment: u32 = max(1, maxVarAlign);
3178 -
    let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign);
3179 -
    let unionLayout = Layout {
3180 -
        size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment),
3181 -
        alignment: unionAlignment
3182 -
    };
3201 +
    let info = computeUnionLayout(&variants[..variantCount]);
3183 3202
3184 3203
    // Update the nominal type with the resolved variants.
3185 3204
    *nominalTy = NominalType::Union(UnionType {
3186 3205
        variants,
3187 3206
        variantsLen: variantCount,
3188 -
        layout: unionLayout,
3189 -
        valOffset: unionValOffset,
3190 -
        isAllVoid,
3207 +
        layout: info.layout,
3208 +
        valOffset: info.valOffset,
3209 +
        isAllVoid: info.isAllVoid,
3191 3210
    });
3192 3211
}
3193 3212
3194 3213
/// Check if a module should be analyzed based on its attributes and build configuration.
3195 3214
fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool {