Fix repeated string slice constant data

8c31c850d6cd809988aec7d72e16582aca5b9770fb984c8a165777997e6f2f25
Constant array-repeat lowering treated every resolver constant as an
inline scalar. Repeated strings were emitted as raw bytes even though
each element required a slice header, leaving missing relocations and
length fields that caused runtime traps.

Route repeated string values through generic per-element constant
lowering so each element receives a complete relocated slice header
backed by deduplicated string data.
Alexis Sellier committed ago 1 parent 60335cc1
lib/std/lang/lower.rad +12 -4
1544 1544
        dataBuilderPush(b, il::DataValue {
1545 1545
            item: il::DataItem::Undef,
1546 1546
            count: elemLayout.size * length
1547 1547
        });
1548 1548
    } else if let val = resolver::constValueEntry(self.resolver, repeat.item) {
1549 -
        dataBuilderPush(b, il::DataValue {
1550 -
            item: constValueToDataItem(self, val, elemTy),
1551 -
            count: length
1552 -
        });
1549 +
        if let case resolver::ConstValue::String(_) = val {
1550 +
            // A string used as a slice is represented by a three-word slice
1551 +
            // header, not by the bytes of the string itself.
1552 +
            for _ in 0..length {
1553 +
                try lowerConstDataInto(self, repeat.item, elemTy, elemLayout.size, dataPrefix, b);
1554 +
            }
1555 +
        } else {
1556 +
            dataBuilderPush(b, il::DataValue {
1557 +
                item: constValueToDataItem(self, val, elemTy),
1558 +
                count: length
1559 +
            });
1560 +
        }
1553 1561
    } else {
1554 1562
        for _ in 0..length {
1555 1563
            try lowerConstDataInto(self, repeat.item, elemTy, elemLayout.size, dataPrefix, b);
1556 1564
        }
1557 1565
    }
test/tests/const.array.repeat.string.slice.rad added +11 -0
1 +
//! returns: 0
2 +
/// Repeated string slices in constant arrays must emit complete slice headers.
3 +
constant WORDS: [*[u8]; 2] = ["abc"; 2];
4 +
5 +
@default fn main() -> i32 {
6 +
    assert WORDS[0].len == 3;
7 +
    assert WORDS[1].len == 3;
8 +
    assert WORDS[0][0] == 'a';
9 +
    assert WORDS[1][2] == 'c';
10 +
    return 0;
11 +
}
test/tests/const.array.repeat.string.slice.ril added +60 -0
1 +
data $WORDS$literal$0 align 1 {
2 +
    str "abc";
3 +
}
4 +
5 +
data $WORDS align 8 {
6 +
    sym $WORDS$literal$0;
7 +
    w32 3;
8 +
    w32 3;
9 +
    sym $WORDS$literal$0;
10 +
    w32 3;
11 +
    w32 3;
12 +
}
13 +
14 +
fn w32 $main() {
15 +
  @entry0
16 +
    copy %0 $WORDS;
17 +
    load w32 %1 %0 8;
18 +
    br.eq w32 %1 3 @assert.ok2 @assert.fail1;
19 +
  @assert.fail1
20 +
    unreachable;
21 +
  @assert.ok2
22 +
    copy %2 $WORDS;
23 +
    mul w64 %3 1 16;
24 +
    add w64 %4 %2 %3;
25 +
    load w32 %5 %4 8;
26 +
    br.eq w32 %5 3 @assert.ok4 @assert.fail3;
27 +
  @assert.fail3
28 +
    unreachable;
29 +
  @assert.ok4
30 +
    copy %6 $WORDS;
31 +
    load w32 %7 %6 8;
32 +
    br.ult w32 0 %7 @guard#pass7 @guard#trap8;
33 +
  @assert.fail5
34 +
    unreachable;
35 +
  @assert.ok6
36 +
    copy %10 $WORDS;
37 +
    mul w64 %11 1 16;
38 +
    add w64 %12 %10 %11;
39 +
    load w32 %13 %12 8;
40 +
    br.ult w32 2 %13 @guard#pass11 @guard#trap12;
41 +
  @guard#pass7
42 +
    load w64 %8 %6 0;
43 +
    load w8 %9 %8 0;
44 +
    br.eq w8 %9 97 @assert.ok6 @assert.fail5;
45 +
  @guard#trap8
46 +
    ebreak;
47 +
    unreachable;
48 +
  @assert.fail9
49 +
    unreachable;
50 +
  @assert.ok10
51 +
    ret 0;
52 +
  @guard#pass11
53 +
    load w64 %14 %12 0;
54 +
    add w64 %15 %14 2;
55 +
    load w8 %16 %15 0;
56 +
    br.eq w8 %16 99 @assert.ok10 @assert.fail9;
57 +
  @guard#trap12
58 +
    ebreak;
59 +
    unreachable;
60 +
}