Require unsafe context for undefined values

5ef2ba405899069b703e595116a505f66deafdfe46fe578292be834469dbaf87
Alexis Sellier committed ago 1 parent 3c86541c
compiler/radiance.rad +19 -19
44 44
/// Main arena size (96 MB) - lives throughout compilation.
45 45
/// Used for: resolver data, types, symbols, global IL data, and codegen output.
46 46
constant MAIN_ARENA_SIZE: u32 = 100663296;
47 47
48 48
/// AST storage arena.
49 -
static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = undefined;
49 +
static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = [0; TEMP_ARENA_SIZE];
50 50
/// Scratch storage reclaimed after each generated function.
51 -
static FN_ARENA: [u8; FN_ARENA_SIZE] = undefined;
51 +
static FN_ARENA: [u8; FN_ARENA_SIZE] = [0; FN_ARENA_SIZE];
52 52
/// Main storage arena - persists throughout compilation.
53 -
static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = undefined;
53 +
static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = [0; MAIN_ARENA_SIZE];
54 54
55 55
/// Module source code.
56 -
static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = undefined;
56 +
static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE];
57 57
/// Module entries for all packages.
58 -
static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined;
58 +
unsafe static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined;
59 59
/// String pool.
60 -
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
60 +
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
61 61
62 62
/// Package scope.
63 -
static RESOLVER_PKG_SCOPE: resolver::Scope = undefined;
63 +
unsafe static RESOLVER_PKG_SCOPE: resolver::Scope = undefined;
64 64
/// Errors emitted by resolver.
65 -
static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined;
65 +
unsafe static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined;
66 66
67 67
/// Code generation storage.
68 -
static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined;
68 +
unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined;
69 69
/// Hash table entries for data symbol lookup.
70 -
static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
70 +
unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
71 71
72 72
/// Debug info file extension.
73 73
constant DEBUG_EXT: *[u8] = ".debug";
74 74
75 75
/// Maximum rodata size (4MB).
77 77
/// Maximum rwdata size (4MB).
78 78
constant MAX_RW_DATA_SIZE: u32 = 4194304;
79 79
/// Maximum path length.
80 80
constant MAX_PATH_LEN: u32 = 256;
81 81
/// Read-only data buffer.
82 -
static RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined;
82 +
static RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE];
83 83
/// Read-write data buffer.
84 -
static RW_DATA_BUF: [u8; MAX_RW_DATA_SIZE] = undefined;
84 +
static RW_DATA_BUF: [u8; MAX_RW_DATA_SIZE] = [0; MAX_RW_DATA_SIZE];
85 85
/// Assembly module source buffer.
86 -
static ASM_SOURCE_BUF: [u8; MAX_SOURCES_SIZE] = undefined;
86 +
static ASM_SOURCE_BUF: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE];
87 87
/// Temporary assembly text buffer.
88 -
static ASM_TEXT_BUF: [u32; 262144] = undefined;
88 +
static ASM_TEXT_BUF: [u32; 262144] = [0; 262144];
89 89
/// Temporary assembly data buffer.
90 -
static ASM_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined;
90 +
static ASM_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE];
91 91
/// Accumulated assembly read-only data.
92 -
static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined;
92 +
static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE];
93 93
94 94
/// Assembly source file extension.
95 95
constant ASM_SOURCE_EXT: *[u8] = ".ras";
96 96
/// Symbol name exported for startup code to call the semantic entry function.
97 97
export constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default";
230 230
/// Create an empty source input set for one package.
231 231
fn packageInput(name: *[u8]) -> PackageInput {
232 232
    return PackageInput {
233 233
        name,
234 234
        startupPath: nil,
235 -
        radPaths: undefined,
235 +
        radPaths: [""; MAX_LOADED_MODULES],
236 236
        radPathCount: 0,
237 -
        asmPaths: undefined,
237 +
        asmPaths: [""; MAX_ASM_MODULES],
238 238
        asmPathCount: 0,
239 239
    };
240 240
}
241 241
242 242
/// Register, load, and parse `path` within `pkg`.
799 799
fn writeDataWithExt(
800 800
    data: *[u8],
801 801
    basePath: *[u8],
802 802
    ext: *[u8]
803 803
) throws (Error) {
804 -
    let mut path: [u8; MAX_PATH_LEN] = undefined;
804 +
    let mut path: [u8; MAX_PATH_LEN] = [0; MAX_PATH_LEN];
805 805
    let mut pos: u32 = 0;
806 806
807 807
    set pos += try! mem::copy(&mut path[pos..], basePath);
808 808
    set pos += try! mem::copy(&mut path[pos..], ext);
809 809
    set path[pos] = 0; // Null-terminate for syscall.
lib/std/arch/rv64/asm/scanner/tests.rad +4 -2
1 1
use std::mem;
2 2
use std::testing;
3 3
use std::lang::strings;
4 4
5 5
/// String pool used by assembler scanner tests.
6 -
static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
6 +
unsafe static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
7 7
8 8
/// Create a scanner for test input.
9 9
fn testScanner(source: *[u8]) -> super::Scanner {
10 -
    return super::scanner(super::SourceKind::String, source, &mut TEST_STRING_POOL);
10 +
    unsafe {
11 +
        return super::scanner(super::SourceKind::String, source, &mut TEST_STRING_POOL);
12 +
    }
11 13
}
12 14
13 15
/// Scanner recognizes assembler-specific sigils and scoped names.
14 16
@test fn testScanRegisterDirectiveAndLabelTokens() throws (testing::TestError) {
15 17
    let mut s = testScanner(
lib/std/arch/rv64/asm/tests.rad +6 -6
9 9
use std::arch::rv64::encode;
10 10
use std::arch::rv64::printer;
11 11
12 12
use super::scanner;
13 13
14 -
static ASM_ARENA_STORAGE: [u8; 65536] = undefined;
15 -
static ASM_TEXT_STORAGE: [u32; 256] = undefined;
16 -
static ASM_DATA_STORAGE: [u8; 1024] = undefined;
17 -
static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18 -
static PRINT_ARENA_STORAGE: [u8; 1024] = undefined;
19 -
static PRINT_BUFFER: [u8; 128] = undefined;
14 +
static ASM_ARENA_STORAGE: [u8; 65536] = [0; 65536];
15 +
static ASM_TEXT_STORAGE: [u32; 256] = [0; 256];
16 +
static ASM_DATA_STORAGE: [u8; 1024] = [0; 1024];
17 +
unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18 +
static PRINT_ARENA_STORAGE: [u8; 1024] = [0; 1024];
19 +
static PRINT_BUFFER: [u8; 128] = [0; 128];
20 20
21 21
unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) {
22 22
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
23 23
    return try super::assemble(
24 24
        scanner::SourceKind::String,
lib/std/arch/rv64/emit.rad +1 -1
143 143
144 144
/// Compute frame layout from local size and used callee-saved registers.
145 145
export fn computeFrame(localSize: i32, usedCalleeSaved: u32, epilogueBlock: u32, isLeaf: bool, isDynamic: bool) -> Frame {
146 146
    let mut frame = Frame {
147 147
        totalSize: 0,
148 -
        savedRegs: undefined,
148 +
        savedRegs: [SavedReg { reg: super::ZERO, offset: 0 }; super::NUM_SAVED_REGISTERS],
149 149
        savedRegsLen: 0,
150 150
        epilogueBlock,
151 151
        isLeaf,
152 152
        isDynamic,
153 153
    };
lib/std/arch/rv64/tests.rad +2 -2
8 8
use std::collections::dict;
9 9
10 10
use super::encode;
11 11
use super::asm;
12 12
13 -
static ASSEMBLY_ARENA_STORAGE: [u8; 16777216] = undefined;
14 -
static ASSEMBLY_TEXT_STORAGE: [u32; 2] = undefined;
13 +
static ASSEMBLY_ARENA_STORAGE: [u8; 16777216] = [0; 16777216];
14 +
static ASSEMBLY_TEXT_STORAGE: [u32; 2] = [0; 2];
15 15
16 16
/// Helper to check encoding equals expected value.
17 17
fn expectEncoding(actual: u32, expected: u32) throws (testing::TestError) {
18 18
    try testing::expect(actual == expected);
19 19
}
lib/std/lang/alloc/tests.rad +8 -8
2 2
3 3
use std::testing;
4 4
5 5
/// Test basic allocation.
6 6
@test fn testAllocBasic() throws (testing::TestError) {
7 -
    static STORAGE: [u8; 64] = undefined;
7 +
    static STORAGE: [u8; 64] = [0; 64];
8 8
    let mut arena = super::new(&mut STORAGE[..]);
9 9
10 10
    let ptr = try! super::alloc(&mut arena, 4, 4);
11 11
    try testing::expect(super::used(&arena) == 4);
12 12
    try testing::expect(super::remaining(&arena) == 60);
13 13
}
14 14
15 15
/// Test that allocations are properly aligned.
16 16
@test fn testAllocAlignment() throws (testing::TestError) {
17 -
    static STORAGE: [u8; 64] = undefined;
17 +
    static STORAGE: [u8; 64] = [0; 64];
18 18
    let mut arena = super::new(&mut STORAGE[..]);
19 19
20 20
    // Allocate 1 byte with 1-byte alignment.
21 21
    let p1 = try! super::alloc(&mut arena, 1, 1);
22 22
    try testing::expect(super::used(&arena) == 1);
26 26
    try testing::expect(super::used(&arena) == 8); // 1 + 3 padding + 4
27 27
}
28 28
29 29
/// Test multiple allocations.
30 30
@test fn testAllocMultiple() throws (testing::TestError) {
31 -
    static STORAGE: [u8; 128] = undefined;
31 +
    static STORAGE: [u8; 128] = [0; 128];
32 32
    let mut arena = super::new(&mut STORAGE[..]);
33 33
34 34
    let p1 = try! super::alloc(&mut arena, 8, 4);
35 35
    let p2 = try! super::alloc(&mut arena, 16, 4);
36 36
    let p3 = try! super::alloc(&mut arena, 4, 4);
38 38
    try testing::expect(super::used(&arena) == 28); // 8 + 16 + 4
39 39
}
40 40
41 41
/// Test that arena throws when exhausted.
42 42
@test fn testAllocExhausted() throws (testing::TestError) {
43 -
    static STORAGE: [u8; 16] = undefined;
43 +
    static STORAGE: [u8; 16] = [0; 16];
44 44
    let mut arena = super::new(&mut STORAGE[..]);
45 45
46 46
    // This should succeed.
47 47
    let p1 = try! super::alloc(&mut arena, 8, 4);
48 48
57 57
    try testing::expect(failed);
58 58
}
59 59
60 60
/// Test that reset allows reuse of memory.
61 61
@test fn testAllocReset() throws (testing::TestError) {
62 -
    static STORAGE: [u8; 32] = undefined;
62 +
    static STORAGE: [u8; 32] = [0; 32];
63 63
    let mut arena = super::new(&mut STORAGE[..]);
64 64
65 65
    let p1 = try! super::alloc(&mut arena, 16, 4);
66 66
    try testing::expect(super::used(&arena) == 16);
67 67
73 73
    let p2 = try! super::alloc(&mut arena, 32, 4);
74 74
}
75 75
76 76
/// Test alignment when offset is already aligned.
77 77
@test fn testAllocAlreadyAligned() throws (testing::TestError) {
78 -
    static STORAGE: [u8; 64] = undefined;
78 +
    static STORAGE: [u8; 64] = [0; 64];
79 79
    let mut arena = super::new(&mut STORAGE[..]);
80 80
81 81
    // Allocate 4 bytes - offset becomes 4, already aligned for next 4-byte alloc.
82 82
    let p1 = try! super::alloc(&mut arena, 4, 4);
83 83
    try testing::expect(super::used(&arena) == 4);
87 87
    try testing::expect(super::used(&arena) == 8);
88 88
}
89 89
90 90
/// Test allocation that would overflow with alignment padding.
91 91
@test fn testAllocOverflowWithPadding() throws (testing::TestError) {
92 -
    static STORAGE: [u8; 16] = undefined;
92 +
    static STORAGE: [u8; 16] = [0; 16];
93 93
    let mut arena = super::new(&mut STORAGE[..]);
94 94
95 95
    // Allocate 1 byte, offset is now 1.
96 96
    let p1 = try! super::alloc(&mut arena, 1, 1);
97 97
104 104
    try testing::expect(failed);
105 105
}
106 106
107 107
/// Test the Allocator interface backed by an arena.
108 108
@test unsafe fn testAllocator() throws (testing::TestError) {
109 -
    static STORAGE: [u8; 256] = undefined;
109 +
    static STORAGE: [u8; 256] = [0; 256];
110 110
    let mut arena = super::new(&mut STORAGE[..]);
111 111
    let a = super::arenaAllocator(&mut arena);
112 112
113 113
    // Allocate through the Allocator indirection.
114 114
    let p1 = a.func(a.ctx, 16, 4);
lib/std/lang/lower.rad +4 -6
845 845
    }
846 846
}
847 847
848 848
/// Finalize lowering and return the unified IL program.
849 849
export fn finalize(low: &Lowerer) -> il::Program {
850 -
    let mut fns: *mut [*il::Fn] = undefined;
851 850
    match low.output {
852 851
        case FnOutput::Accumulate(accumulated) => {
853 -
            set fns = accumulated;
852 +
            return il::Program {
853 +
                data: &low.data[..],
854 +
                fns: &accumulated[..],
855 +
            };
854 856
        }
855 857
        case FnOutput::Stream(_) => {
856 858
            panic "finalize: cannot finalize streaming lowerer";
857 859
        }
858 860
    }
859 -
    return il::Program {
860 -
        data: &low.data[..],
861 -
        fns: &fns[..],
862 -
    };
863 861
}
864 862
865 863
/////////////////////////////////
866 864
// Qualified Name Construction //
867 865
/////////////////////////////////
lib/std/lang/module.rad +3 -3
285 285
    let pathSuffix = mem::stripPrefix(baseDir, filePath) else {
286 286
        throw ModuleError::InvalidPath;
287 287
    };
288 288
289 289
    // Parse the stripped path into components.
290 -
    let mut parts: [*[u8]; MAX_MODULE_PATH_DEPTH] = undefined;
290 +
    let mut parts: [*[u8]; MAX_MODULE_PATH_DEPTH] = [""; MAX_MODULE_PATH_DEPTH];
291 291
    let partsLen = parsePath(pathSuffix, &mut parts[..]) else {
292 292
        throw ModuleError::InvalidPath;
293 293
    };
294 294
    if partsLen == 0 {
295 295
        throw ModuleError::InvalidPath;
331 331
        packageId,
332 332
        parent: nil,
333 333
        filePath,
334 334
        dirLen: 0,
335 335
        name: strings::intern(graph.pool, name),
336 -
        path: undefined,
336 +
        path: [""; MAX_MODULE_PATH_DEPTH],
337 337
        pathDepth: 0,
338 338
        state: ModuleState::Vacant,
339 -
        children: undefined,
339 +
        children: [0; MAX_MODULES],
340 340
        childrenLen: 0,
341 341
        ast: nil,
342 342
        source: nil,
343 343
    };
344 344
    set m.dirLen = dirLength(m.filePath);
lib/std/lang/module/tests.rad +5 -5
4 4
use std::testing;
5 5
use std::lang::ast;
6 6
use std::lang::strings;
7 7
8 8
/// Test arena backing storage.
9 -
static TEST_ARENA: [u8; 4096] = undefined;
9 +
static TEST_ARENA: [u8; 4096] = [0; 4096];
10 10
/// Interned string pool.
11 -
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
11 +
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
12 12
13 13
fn expectSliceEq(actual: *[u8], expected: *[u8])
14 14
    throws (testing::TestError)
15 15
{
16 16
    if not mem::eq(actual, expected) {
114 114
    try testing::expect(result == nil);
115 115
}
116 116
117 117
@test fn testParsePathSingleComponent() throws (testing::TestError) {
118 118
    let path = "std.rad";
119 -
    let mut components: [*[u8]; 8] = undefined;
119 +
    let mut components: [*[u8]; 8] = [""; 8];
120 120
    let count = super::parsePath(path, &mut components[..]) else {
121 121
        throw testing::TestError::Failed;
122 122
    };
123 123
    try testing::expect(count == 1);
124 124
    try expectSliceEq(components[0], "std");
125 125
}
126 126
127 127
@test fn testParsePathMultipleComponents() throws (testing::TestError) {
128 128
    let path = "std/lang/parser.rad";
129 -
    let mut components: [*[u8]; 8] = undefined;
129 +
    let mut components: [*[u8]; 8] = [""; 8];
130 130
    let count = super::parsePath(path, &mut components[..]) else {
131 131
        throw testing::TestError::Failed;
132 132
    };
133 133
    try testing::expect(count == 3);
134 134
    try expectSliceEq(components[0], "std");
136 136
    try expectSliceEq(components[2], "parser");
137 137
}
138 138
139 139
@test fn testParsePathWithoutExtension() throws (testing::TestError) {
140 140
    let path = "std/lang/parser";
141 -
    let mut components: [*[u8]; 8] = undefined;
141 +
    let mut components: [*[u8]; 8] = [""; 8];
142 142
    let result = super::parsePath(path, &mut components[..]);
143 143
    try testing::expect(result == nil);
144 144
}
145 145
146 146
@test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) {
lib/std/lang/parser/tests.rad +2 -2
12 12
use std::lang::strings;
13 13
14 14
/// Unified arena size.
15 15
constant ARENA_SIZE: u32 = 2097152;
16 16
/// Unified arena storage for all AST allocations.
17 -
static ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
17 +
static ARENA_STORAGE: [u8; ARENA_SIZE] = [0; ARENA_SIZE];
18 18
/// String pool.
19 -
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
19 +
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
20 20
21 21
/// Assert that a node is an identifier with the given name.
22 22
fn expectIdent(node: *ast::Node, name: *[u8])
23 23
    throws (testing::TestError)
24 24
{
lib/std/lang/resolver.rad +1 -0
2955 2955
                return setNodeType(self, node, hint);
2956 2956
            }
2957 2957
            return setNodeType(self, node, Type::Nil);
2958 2958
        },
2959 2959
        case ast::NodeValue::Undef => {
2960 +
            try requireUnsafe(self, node);
2960 2961
            return setNodeType(self, node, Type::Undefined);
2961 2962
        },
2962 2963
        case ast::NodeValue::Bool(value) => {
2963 2964
            setNodeConstValue(self, node, ConstValue::Bool(value));
2964 2965
            return setNodeType(self, node, Type::Bool);
lib/std/lang/resolver/tests.rad +65 -25
12 12
13 13
/// Synthetic file path used for resolver tests.
14 14
constant MODULE_PATH: *[u8] = "/dev/test.rad";
15 15
16 16
/// AST arena storage used by resolver tests.
17 -
static AST_ARENA: [u8; 2097152] = undefined;
17 +
static AST_ARENA: [u8; 2097152] = [0; 2097152];
18 18
19 19
/// Resolver arena storage used by resolver tests.
20 -
static ARENA_STORAGE: [u8; 2097152] = undefined;
20 +
static ARENA_STORAGE: [u8; 2097152] = [0; 2097152];
21 21
22 22
/// Node metadata storage used by resolver tests.
23 -
static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined;
23 +
unsafe static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined;
24 24
25 25
/// Diagnostic storage used by resolver tests.
26 -
static ERROR_STORAGE: [super::Error; 16] = undefined;
26 +
unsafe static ERROR_STORAGE: [super::Error; 16] = undefined;
27 27
28 28
/// Package scope used by resolver tests.
29 -
static PKG_SCOPE: super::Scope = undefined;
29 +
unsafe static PKG_SCOPE: super::Scope = undefined;
30 30
31 31
/// Module entries used by resolver tests.
32 -
static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined;
32 +
unsafe static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined;
33 33
34 34
/// Module graph used by resolver tests.
35 -
static MODULE_GRAPH: module::ModuleGraph = undefined;
35 +
unsafe static MODULE_GRAPH: module::ModuleGraph = undefined;
36 36
37 37
/// Module AST arena storage used by resolver tests.
38 -
static MODULE_ARENA_STORAGE: [u8; 4096] = undefined;
38 +
static MODULE_ARENA_STORAGE: [u8; 4096] = [0; 4096];
39 39
40 40
/// Module AST arena used by resolver tests.
41 -
static MODULE_ARENA: ast::NodeArena = undefined;
41 +
unsafe static MODULE_ARENA: ast::NodeArena = undefined;
42 42
43 43
/// Interned string pool used by resolver tests.
44 -
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
44 +
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
45 45
46 46
/// String literals used in tests.
47 47
constant LITERALS: [*[u8]; 15] = [
48 48
    "Ok", "Error", "R", "S",
49 49
    "f", "Status", "Pending",
57 57
    diagnostics: super::Diagnostics,
58 58
    root: *ast::Node,
59 59
}
60 60
61 61
/// Create isolated storage for tests to avoid conflicts with global resolver storage.
62 -
fn testStorage() -> super::ResolverStorage {
62 +
unsafe fn testStorage() -> super::ResolverStorage {
63 63
    return super::ResolverStorage {
64 64
        arena: alloc::new(&mut ARENA_STORAGE[..]),
65 65
        nodeData: &mut NODE_DATA_STORAGE[..],
66 66
        pkgScope: &mut PKG_SCOPE,
67 67
        errors: &mut ERROR_STORAGE[..],
2154 2154
}
2155 2155
2156 2156
@test unsafe fn testUndefinedCoercions() throws (testing::TestError) {
2157 2157
    {
2158 2158
        let mut a = testResolver();
2159 -
        let result = try resolveBlockStr(&mut a, "let count: i32 = undefined;");
2159 +
        let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }");
2160 2160
        try expectNoErrors(&result);
2161 2161
    } {
2162 2162
        let mut a = testResolver();
2163 -
        let program = "let mut value: i32 = 0; set value = undefined;";
2163 +
        let program = "unsafe { let mut value: i32 = 0; set value = undefined; }";
2164 2164
        let result = try resolveProgramStr(&mut a, program);
2165 2165
        try expectNoErrors(&result);
2166 2166
    } {
2167 2167
        let mut a = testResolver();
2168 -
        let program = "fn f(x: i32) {} fn g() { f(undefined); }";
2168 +
        let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }";
2169 2169
        let result = try resolveProgramStr(&mut a, program);
2170 2170
        try expectNoErrors(&result);
2171 2171
    } {
2172 2172
        let mut a = testResolver();
2173 -
        let program = "fn fetch() -> i32 { return undefined; }";
2173 +
        let program = "unsafe fn fetch() -> i32 { return undefined; }";
2174 2174
        let result = try resolveProgramStr(&mut a, program);
2175 2175
        try expectNoErrors(&result);
2176 2176
    }
2177 2177
}
2178 2178
3672 3672
        let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}");
3673 3673
        let err = try expectError(&result);
3674 3674
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3675 3675
    } {
3676 3676
        let mut a = testResolver();
3677 -
        let result = try resolveProgramStr(&mut a, "fn f() { let x: opaque = undefined; }");
3677 +
        let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }");
3678 3678
        let err = try expectError(&result);
3679 3679
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3680 3680
    } {
3681 3681
        let mut a = testResolver();
3682 3682
        let result = try resolveProgramStr(&mut a, "record R { x: opaque }");
3832 3832
    let mut a = testResolver();
3833 3833
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3834 3834
3835 3835
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3836 3836
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena);
3837 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = undefined;", &mut arena);
3837 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena);
3838 3838
3839 3839
    let result = try resolveModuleTree(&mut a, rootId);
3840 3840
    try expectNoErrors(&result);
3841 3841
}
3842 3842
4500 4500
        let mut a = testResolver();
4501 4501
        let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;");
4502 4502
        try expectNoErrors(&result);
4503 4503
    } { // Function pointer to numeric.
4504 4504
        let mut a = testResolver();
4505 -
        let result = try resolveBlockStr(&mut a, "let f: fn() = undefined; f as u32;");
4505 +
        let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }");
4506 4506
        try expectNoErrors(&result);
4507 4507
    } { // *u8 to *i32 (u8 to i32 is valid).
4508 4508
        let mut a = testResolver();
4509 4509
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }");
4510 4510
        try expectNoErrors(&result);
4514 4514
        try expectNoErrors(&result);
4515 4515
    }
4516 4516
4517 4517
    { // *[i32] to *[opaque].
4518 4518
        let mut a = testResolver();
4519 -
        let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *[opaque];");
4519 +
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }");
4520 4520
        try expectNoErrors(&result);
4521 4521
    } { // *[opaque] to *[i32].
4522 4522
        let mut a = testResolver();
4523 4523
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }");
4524 4524
        try expectNoErrors(&result);
4544 4544
        try expectNoErrors(&result);
4545 4545
    }
4546 4546
4547 4547
    { // Identity cast: *mut [i32] to *mut [i32].
4548 4548
        let mut a = testResolver();
4549 -
        let result = try resolveBlockStr(&mut a, "let s: *mut [i32] = undefined; s as *mut [i32];");
4549 +
        let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }");
4550 4550
        try expectNoErrors(&result);
4551 4551
    } { // Identity cast: *i32 to *i32.
4552 4552
        let mut a = testResolver();
4553 -
        let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *i32;");
4553 +
        let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }");
4554 4554
        try expectNoErrors(&result);
4555 4555
    } { // Identity cast: i32 to i32.
4556 4556
        let mut a = testResolver();
4557 4557
        let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;");
4558 4558
        try expectNoErrors(&result);
4561 4561
4562 4562
/// Tests for invalid `as` casts that should be rejected.
4563 4563
@test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) {
4564 4564
    { // Pointer to slice is invalid.
4565 4565
        let mut a = testResolver();
4566 -
        let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *[i32];");
4566 +
        let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }");
4567 4567
        let err = try expectError(&result);
4568 4568
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4569 4569
            else throw testing::TestError::Failed;
4570 4570
    } { // Slice to pointer is invalid.
4571 4571
        let mut a = testResolver();
4572 -
        let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *i32;");
4572 +
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }");
4573 4573
        let err = try expectError(&result);
4574 4574
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4575 4575
            else throw testing::TestError::Failed;
4576 4576
    } { // *T to *i32 is invalid.
4577 4577
        let mut a = testResolver();
4585 4585
        let err = try expectError(&result);
4586 4586
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4587 4587
            else throw testing::TestError::Failed;
4588 4588
    } { // Slice to numeric is invalid.
4589 4589
        let mut a = testResolver();
4590 -
        let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as u32;");
4590 +
        let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }");
4591 4591
        let err = try expectError(&result);
4592 4592
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4593 4593
            else throw testing::TestError::Failed;
4594 4594
    } { // *record to *u8 is invalid.
4595 4595
        let mut a = testResolver();
5099 5099
    let mut a = testResolver();
5100 5100
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5101 5101
5102 5102
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5103 5103
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena);
5104 -
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = undefined;", &mut arena);
5104 +
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena);
5105 5105
5106 5106
    let result = try resolveModuleTree(&mut a, rootId);
5107 5107
    try expectNoErrors(&result);
5108 5108
}
5109 5109
5680 5680
    let mut b = testResolver();
5681 5681
    let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];");
5682 5682
    try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
5683 5683
}
5684 5684
5685 +
/// Uninitialized values require an explicit unsafe context in every value position.
5686 +
@test unsafe fn testUndefinedRequiresUnsafe() throws (testing::TestError) {
5687 +
    let programs = &[
5688 +
        "fn run() -> u8 { let pointers: [*u8; 1] = undefined; return *pointers[0]; }",
5689 +
        "fn run() { let n: u32 = undefined; }",
5690 +
        "fn run() -> *u8 { return undefined; }",
5691 +
        "fn take(p: *u8) {} fn run() { take(undefined); }",
5692 +
        "fn run(p: *u8) { let mut q = p; set q = undefined; }",
5693 +
        "fn run() { let pointers: [*u8; 1] = [undefined]; }",
5694 +
        "fn run() { let pointers: [*u8; 2] = [undefined; 2]; }",
5695 +
        "record R: Copy { p: *u8 } fn run() { let r = R { p: undefined }; }",
5696 +
        "union U: Copy { Value { p: *u8 } } fn run() { let u = U::Value { p: undefined }; }",
5697 +
        "static P: *u8 = undefined;",
5698 +
        "constant P: *u8 = undefined;",
5699 +
        "static DATA: [u8; 4] = undefined;",
5700 +
        "record R: Copy { p: *u8 } static VALUE: R = R { p: undefined };",
5701 +
    ];
5702 +
    for program in programs {
5703 +
        let mut a = testResolver();
5704 +
        let result = try resolveProgramStr(&mut a, program);
5705 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5706 +
    }
5707 +
    try expectAnalyzeOk("unsafe fn run() { let p: *u8 = undefined; }");
5708 +
    try expectAnalyzeOk("fn run() -> u32 { unsafe { let mut n: u32 = undefined; set n = 7; return n; } }");
5709 +
    try expectAnalyzeOk("unsafe static P: *u8 = undefined;");
5710 +
    try expectAnalyzeOk("record R: Copy { p: *u8 } unsafe static VALUE: R = R { p: undefined };");
5711 +
    try expectAnalyzeOk("fn run() { let data: [u8; 4] = [0; 4]; let pointer: ?*u8 = nil; }");
5712 +
    try expectAnalyzeOk("static DATA: [u8; 4] = [0; 4];");
5713 +
}
5714 +
5715 +
/// Unsafe initialization does not grant permission to subsequent safe expressions.
5716 +
@test unsafe fn testUndefinedContextRestored() throws (testing::TestError) {
5717 +
    let mut a = testResolver();
5718 +
    let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }");
5719 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5720 +
    let mut b = testResolver();
5721 +
    let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;");
5722 +
    try expectErrorKind(&next, super::ErrorKind::UnsafeOperation);
5723 +
}
5724 +
5685 5725
/// Unsafe declarations may compose unsafe operations and calls.
5686 5726
@test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5687 5727
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }";
5688 5728
    try expectAnalyzeOk(program);
5689 5729
}
lib/std/lang/scanner/tests.rad +6 -9
1 1
use std::lang::strings;
2 2
use std::mem;
3 3
use std::testing;
4 4
5 5
/// String pool for testing.
6 -
static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
6 +
unsafe static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
7 7
8 +
/// Create a scanner backed by the test string pool.
8 9
fn testScanner(source: *[u8]) -> super::Scanner {
9 -
    return super::scanner(super::SourceLoc::File("test.r"), source, &mut TEST_STRING_POOL);
10 +
    unsafe {
11 +
        return super::scanner(super::SourceLoc::File("test.r"), source, &mut TEST_STRING_POOL);
12 +
    }
10 13
}
11 14
12 15
@test fn testScanTokens() throws (testing::TestError) {
13 16
    let mut s = testScanner(
14 17
        "'x' < fnord fnord: 0 => >> mod and nil"
127 130
        try testing::expect(false);
128 131
    }
129 132
}
130 133
131 134
@test fn testScanEmptyInput() throws (testing::TestError) {
132 -
    let mut s = super::Scanner {
133 -
        sourceLoc: super::SourceLoc::File("test.r"),
134 -
        source: "",
135 -
        token: 0,
136 -
        cursor: 0,
137 -
        pool: &mut TEST_STRING_POOL,
138 -
    };
135 +
    let mut s = testScanner("");
139 136
    let tok: super::Token = super::next(&mut s);
140 137
141 138
    try testing::expect(tok.kind == super::TokenKind::Eof);
142 139
    try testing::expect(tok.source.len == 0);
143 140
    try testing::expect(tok.offset == 0);
lib/std/tests.rad +1 -1
253 253
    try testing::expect(threw);
254 254
}
255 255
256 256
@test fn testWriteFileParts() throws (testing::TestError) {
257 257
    let path = "/tmp/radiance-std-write-file-parts.test";
258 -
    let mut buffer: [u8; 16] = undefined;
258 +
    let mut buffer: [u8; 16] = [0; 16];
259 259
260 260
    if not unix::writeFileParts(path, &["hello", " ", "world"]) {
261 261
        throw testing::TestError::Failed;
262 262
    }
263 263
    let data = unix::readFile(path, &mut buffer[..]) else {
test/runner.rad +11 -11
33 33
constant SOURCE_EXT: *[u8] = ".rad";
34 34
/// IL snapshot file extension for binary tests.
35 35
constant SNAPSHOT_EXT: *[u8] = ".ril";
36 36
37 37
/// String pool.
38 -
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
38 +
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
39 39
40 40
/// Maximum number of AST nodes per test file.
41 41
constant MAX_NODE_DATA: u32 = 4096;
42 42
/// Maximum number of resolver errors per test file.
43 43
constant MAX_ERRORS: u32 = 16;
46 46
/// Maximum number of data bytes in a `.ras` test binary.
47 47
constant ASM_DATA_CAPACITY: u32 = 1024;
48 48
49 49
// Static storage for large buffers to avoid stack overflow.
50 50
// Tests run serially so sharing these is safe.
51 -
static SOURCE_BUF: [u8; SOURCE_BUF_SIZE] = undefined;
52 -
static EXPECTED_BUF: [u8; EXPECTED_BUF_SIZE] = undefined;
53 -
static OUTPUT_BUF: [u8; OUTPUT_BUF_SIZE] = undefined;
54 -
static AST_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
55 -
static IL_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
56 -
static RESOLVER_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
57 -
static NODE_DATA_STORAGE: [resolver::NodeData; MAX_NODE_DATA] = undefined;
58 -
static ERROR_STORAGE: [resolver::Error; MAX_ERRORS] = undefined;
59 -
static ASM_TEXT_STORAGE: [u32; ASM_TEXT_CAPACITY] = undefined;
60 -
static ASM_DATA_STORAGE: [u8; ASM_DATA_CAPACITY] = undefined;
51 +
unsafe static SOURCE_BUF: [u8; SOURCE_BUF_SIZE] = undefined;
52 +
unsafe static EXPECTED_BUF: [u8; EXPECTED_BUF_SIZE] = undefined;
53 +
unsafe static OUTPUT_BUF: [u8; OUTPUT_BUF_SIZE] = undefined;
54 +
unsafe static AST_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
55 +
unsafe static IL_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
56 +
unsafe static RESOLVER_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined;
57 +
unsafe static NODE_DATA_STORAGE: [resolver::NodeData; MAX_NODE_DATA] = undefined;
58 +
unsafe static ERROR_STORAGE: [resolver::Error; MAX_ERRORS] = undefined;
59 +
unsafe static ASM_TEXT_STORAGE: [u32; ASM_TEXT_CAPACITY] = undefined;
60 +
unsafe static ASM_DATA_STORAGE: [u8; ASM_DATA_CAPACITY] = undefined;
61 61
62 62
/// Strip a `//` comment from a line, preserving `//` inside quoted strings.
63 63
/// Returns the content before the comment, trimmed of trailing whitespace.
64 64
fn stripLine(line: *[u8]) -> *[u8] {
65 65
    let mut end = line.len;
test/tests/const-expr-array-size.rad +2 -2
5 5
6 6
constant ROWS: u32 = 3;
7 7
constant COLS: u32 = 4;
8 8
constant TOTAL: u32 = ROWS * COLS;
9 9
10 -
static DATA: [i32; TOTAL] = undefined;
10 +
unsafe static DATA: [i32; TOTAL] = undefined;
11 11
12 -
@default fn main() -> i32 {
12 +
@default unsafe fn main() -> i32 {
13 13
    // Verify the array has the expected length.
14 14
    if DATA.len <> 12 { return 1; }
15 15
    return 0;
16 16
}
test/tests/const-expr-cast.rad +2 -2
10 10
constant SHIFTED: u64 = 4 as u64;
11 11
12 12
// Chained casts.
13 13
constant LEN: u32 = 8;
14 14
constant LEN2: u32 = (LEN as u64) as u32;
15 -
static BUF: [u8; LEN2] = undefined;
15 +
unsafe static BUF: [u8; LEN2] = undefined;
16 16
17 17
// Cast of unsuffixed literal arithmetic.
18 18
constant E: u32 = (3 + 4) as u32;
19 19
// Nested casts of literal arithmetic.
20 20
constant F: u32 = ((3 + 4) as u64) as u32;
30 30
// Typed constant * typed constant through cast.
31 31
constant X: u8 = 3;
32 32
constant Y: u8 = 4;
33 33
constant Z: i32 = (X as i32) * (Y as i32);
34 34
35 -
@default fn main() -> i32 {
35 +
@default unsafe fn main() -> i32 {
36 36
    assert BUF.len == 8;
37 37
    assert WIDE == 255;
38 38
    assert Z == 12;
39 39
    assert E == 7;
40 40
    assert F == 7;
test/tests/const-expr-literal.rad +3 -3
13 13
constant E: u32 = 2 * 3 + 4;
14 14
// Unary negation with literal.
15 15
constant F: i32 = -5;
16 16
constant G: i32 = F * 2;
17 17
18 -
static BUF: [u8; A] = undefined;
19 -
static BUF2: [u8; C] = undefined;
18 +
unsafe static BUF: [u8; A] = undefined;
19 +
unsafe static BUF2: [u8; C] = undefined;
20 20
21 -
@default fn main() -> i32 {
21 +
@default unsafe fn main() -> i32 {
22 22
    assert A == 16;
23 23
    assert BUF.len == 16;
24 24
    assert C == 20;
25 25
    assert BUF2.len == 20;
26 26
    assert D == 10;
test/tests/edge.cases.6.rad +5 -5
55 55
    q: 0x1234560C,
56 56
    r: 0x1234560D,
57 57
    s: 0x1234560E,
58 58
    t: 0x1234560F,
59 59
};
60 -
static ANALYZER: Analyzer = undefined;
60 +
unsafe static ANALYZER: Analyzer = undefined;
61 61
62 62
fn fillEntry(out: *mut Entry) {
63 63
    set *out = DEFAULT_ENTRY;
64 64
}
65 65
72 72
        return nil;
73 73
    }
74 74
    return mkEntry();
75 75
}
76 76
77 -
fn init(entries: *mut [Entry]) {
77 +
unsafe fn init(entries: *mut [Entry]) {
78 78
    set ANALYZER = Analyzer {
79 79
        pad0: 0xDEADAAA0,
80 80
        pad1: 0xDEADAAA1,
81 81
        entries,
82 82
        len: 0,
83 83
    };
84 84
}
85 85
86 -
fn add() {
86 +
unsafe fn add() {
87 87
    let idx = ANALYZER.len;
88 88
    let entry = mkOptEntry(true) else {
89 89
        return;
90 90
    };
91 91
    set ANALYZER.entries[idx] = entry;
92 92
    set ANALYZER.len = idx + 1;
93 93
}
94 94
95 -
fn checkHeader(expected: *[Entry]) -> i32 {
95 +
unsafe fn checkHeader(expected: *[Entry]) -> i32 {
96 96
    unsafe {
97 97
        if ANALYZER.entries.ptr <> expected.ptr or ANALYZER.entries.len <> expected.len {
98 98
            // Slice header got clobbered instead of the backing storage.
99 99
            return 1;
100 100
        }
111 111
        return 3;
112 112
    }
113 113
    return 0;
114 114
}
115 115
116 -
@default fn main() -> i32 {
116 +
@default unsafe fn main() -> i32 {
117 117
    let target = &mut STORAGE[..];
118 118
    init(target);
119 119
    add();
120 120
    return checkHeader(target);
121 121
}
test/tests/edge.cases.7.addr.bug.rad +2 -2
3 3
record PtrHolder: Copy {
4 4
    ptr: *mut i32,
5 5
}
6 6
7 7
static target: i32 = 10;
8 -
static holder: PtrHolder = undefined;
8 +
unsafe static holder: PtrHolder = undefined;
9 9
10 -
@default fn main() -> i32 {
10 +
@default unsafe fn main() -> i32 {
11 11
    set holder.ptr = &mut target;
12 12
    set *holder.ptr = 42;
13 13
14 14
    return target;
15 15
}
test/tests/edge.cases.8.bug.rad +2 -2
10 10
record Outer: Copy {
11 11
    padding: i32,
12 12
    inner: Inner,
13 13
}
14 14
15 -
static global: Outer = undefined;
15 +
unsafe static global: Outer = undefined;
16 16
17 17
fn readInner(i: Inner) -> i32 {
18 18
    return i.value;
19 19
}
20 20
21 -
@default fn main() -> i32 {
21 +
@default unsafe fn main() -> i32 {
22 22
    set global = Outer { padding: 0, inner: Inner { value: 42 } };
23 23
24 24
    assert readInner(global.inner) == 42;
25 25
    return 0;
26 26
}
test/tests/for.else.continue.rad +1 -1
23 23
        }
24 24
    }
25 25
    return nil;
26 26
}
27 27
28 -
@default fn main() -> i32 {
28 +
@default unsafe fn main() -> i32 {
29 29
    let mut fields: [Field; 4] = undefined;
30 30
    set fields[0] = Field { name: nil, value: 10 };
31 31
    set fields[1] = Field { name: "foo", value: 20 };
32 32
    set fields[2] = Field { name: nil, value: 30 };
33 33
    set fields[3] = Field { name: "bar", value: 40 };
test/tests/frame.large.rad +4 -4
1 1
//! returns: 0
2 2
//! Test large stack frames and addressing near top of frame.
3 -
fn bigFrame1() -> i32 {
3 +
unsafe fn bigFrame1() -> i32 {
4 4
    let pad: [u8; 2024] = undefined;
5 5
    let n: i32 = 491823;
6 6
7 7
    return n + 1;
8 8
}
9 9
10 -
fn bigFrame2() -> i32 {
10 +
unsafe fn bigFrame2() -> i32 {
11 11
    let pad: [u8; 2028] = undefined;
12 12
    let arr: [i32; 4] = [1, 2, 3, 4];
13 13
14 14
    return arr[0] + arr[1] + arr[3];
15 15
}
16 16
17 -
fn bigFrame3() -> i32 {
17 +
unsafe fn bigFrame3() -> i32 {
18 18
    let mut ary: [u8; 4096] = undefined;
19 19
    set ary[4091] = 192;
20 20
21 21
    return ary[4091] as i32;
22 22
}
23 23
24 -
@default fn main() -> i32 {
24 +
@default unsafe fn main() -> i32 {
25 25
    assert bigFrame1() == 491824;
26 26
    assert bigFrame2() == 7;
27 27
    assert bigFrame3() == 192;
28 28
    return 0;
29 29
}
test/tests/large.blit.store.rad +4 -4
19 19
}
20 20
21 21
/// Store at offset 2200 (> MAX_IMM) into a record field.
22 22
/// Generates `store w32 <imm> %0 2200` in IL, which triggers the
23 23
/// SCRATCH1 aliasing bug when adjustOffset clobbers the value.
24 -
fn storeLargeOffset() -> i32 {
24 +
unsafe fn storeLargeOffset() -> i32 {
25 25
    let mut b: Big = undefined;
26 26
    set b.tag = 42;
27 27
    assert b.tag == 42;
28 28
    set b.tag = 99;
29 29
    assert b.tag == 99;
30 30
    return 0;
31 31
}
32 32
33 33
/// Copy a >2047 byte struct (triggers blit offset overflow).
34 -
fn copyBig() -> i32 {
34 +
unsafe fn copyBig() -> i32 {
35 35
    let mut src: Big = undefined;
36 36
    set src.a[0] = 10;
37 37
    set src.a[1000] = 20;
38 38
    set src.a[2199] = 30;
39 39
    set src.tag = 77;
46 46
    assert dst.tag == 77;
47 47
    return 0;
48 48
}
49 49
50 50
/// Mutate a copy to ensure the blit produced an independent copy.
51 -
fn copyIndependence() -> i32 {
51 +
unsafe fn copyIndependence() -> i32 {
52 52
    let mut a: Big = undefined;
53 53
    set a.a[0] = 1;
54 54
    set a.a[2199] = 2;
55 55
    set a.tag = 100;
56 56
64 64
    assert b.a[2199] == 2;
65 65
    assert b.tag == 200;
66 66
    return 0;
67 67
}
68 68
69 -
@default fn main() -> i32 {
69 +
@default unsafe fn main() -> i32 {
70 70
    let r1 = storeLargeOffset();
71 71
    if r1 <> 0 {
72 72
        return 10 + r1;
73 73
    }
74 74
test/tests/loc.addr.offset.bug.rad +2 -2
9 9
record Outer: Copy {
10 10
    pad: i32,      // offset 0, size 4
11 11
    inner: Inner,  // offset 4, size 4
12 12
}
13 13
14 -
static outer: Outer = undefined;
14 +
unsafe static outer: Outer = undefined;
15 15
16 -
@default fn main() -> i32 {
16 +
@default unsafe fn main() -> i32 {
17 17
    set outer.inner.value = 42;
18 18
19 19
    let ptr: *Inner = &outer.inner;
20 20
21 21
    assert (*ptr).value == 42;
test/tests/loc.addr.opt.to.opt.rad +2 -2
5 5
record Container: Copy {
6 6
    pad: i32,           // offset 0
7 7
    opt: ?i32,          // offset 4
8 8
}
9 9
10 -
static container: Container = undefined;
10 +
unsafe static container: Container = undefined;
11 11
12 -
@default fn main() -> i32 {
12 +
@default unsafe fn main() -> i32 {
13 13
    let sourceOpt: ?i32 = 42;
14 14
15 15
    set container.opt = sourceOpt;
16 16
17 17
    if let val = container.opt {
test/tests/loc.addr.optional.assign.rad +2 -2
4 4
record Container: Copy {
5 5
    pad: i32,           // offset 0, size 4
6 6
    opt: ?i32,          // offset 4, size 8 (tag + value)
7 7
}
8 8
9 -
static container: Container = undefined;
9 +
unsafe static container: Container = undefined;
10 10
11 -
@default fn main() -> i32 {
11 +
@default unsafe fn main() -> i32 {
12 12
    set container.opt = 42;
13 13
14 14
    if let val = container.opt {
15 15
        assert val == 42;
16 16
        return 0;
test/tests/loc.addr.record.assign.rad +3 -3
9 9
record Outer: Copy {
10 10
    pad: i32,      // offset 0, size 4
11 11
    inner: Inner,  // offset 4, size 4
12 12
}
13 13
14 -
static outer: Outer = undefined;
15 -
static source: Inner = undefined;
14 +
unsafe static outer: Outer = undefined;
15 +
unsafe static source: Inner = undefined;
16 16
17 -
@default fn main() -> i32 {
17 +
@default unsafe fn main() -> i32 {
18 18
    set source.value = 42;
19 19
20 20
    set outer.inner = source;
21 21
22 22
    assert outer.inner.value == 42;
test/tests/memzero.union.bug.rad +1 -1
10 10
    guard1: u16,
11 11
    val: Payload,
12 12
    guard2: u32,
13 13
}
14 14
15 -
@default fn main() -> i32 {
15 +
@default unsafe fn main() -> i32 {
16 16
    let mut f: Frame = Frame { guard1: 0xDEAD, val: undefined, guard2: 0xDEADBEEF };
17 17
18 18
    set f.val = Payload::Small(7);
19 19
    set f.val = Payload::Big([1, 2, 3]);
20 20
test/tests/pointer.slice.store.rad +3 -3
16 16
17 17
record PtrBox: Copy {
18 18
    ptr: *mut *mut [Entry],
19 19
}
20 20
21 -
static STORAGE: [Entry; 2] = undefined;
22 -
static TABLE: Table = undefined;
23 -
static HOLDER: PtrBox = undefined;
21 +
unsafe static STORAGE: [Entry; 2] = undefined;
22 +
unsafe static TABLE: Table = undefined;
23 +
unsafe static HOLDER: PtrBox = undefined;
24 24
25 25
/// Exercise slice storage and inspect its pointer fields.
26 26
@default unsafe fn main() -> i32 {
27 27
    set TABLE.entries = &mut STORAGE[..];
28 28
    set TABLE.len     = 0;
test/tests/record.copy.rad +3 -3
69 69
    assert a[2].x == 561;
70 70
    assert a[3].x == 12;
71 71
    return 0;
72 72
}
73 73
74 -
fn func4(s: S) -> i32 {
74 +
unsafe fn func4(s: S) -> i32 {
75 75
    let mut a: [S; 2] = undefined;
76 76
    set a[0] = s;
77 77
78 78
    assert a[0].x == 561;
79 79
    assert a[0].y == 938;
80 80
    assert a[0].z == 102;
81 81
    return 0;
82 82
}
83 83
84 -
fn func5(s: S) -> i32 {
84 +
unsafe fn func5(s: S) -> i32 {
85 85
    let mut a: [S; 2] = undefined;
86 86
    let t: S = makeS(s.x, s.y, s.z);
87 87
    set a[0] = t;
88 88
89 89
    assert a[0].x == 561;
90 90
    assert a[0].y == 938;
91 91
    assert a[0].z == 102;
92 92
    return 0;
93 93
}
94 94
95 -
@default fn main() -> i32 {
95 +
@default unsafe fn main() -> i32 {
96 96
    let s: S = S { x: 561, y: 938, z: 102 };
97 97
98 98
    let r1: i32 = func1(s);
99 99
    if r1 <> 0 {
100 100
        return 10 + r1;
test/tests/slice.append.rad +1 -1
39 39
        func: arenaAllocFn,
40 40
        ctx: arena as *mut opaque,
41 41
    };
42 42
}
43 43
44 -
static BUF: [u8; 4096] = undefined;
44 +
unsafe static BUF: [u8; 4096] = undefined;
45 45
46 46
@default unsafe fn main() -> i32 {
47 47
    static arena: Arena = undefined;
48 48
    set arena = newArena(&mut BUF[..]);
49 49
    let a = arenaAllocator(&mut arena);
test/tests/static.slice.index.assign.rad +2 -2
7 7
record Container: Copy {
8 8
    pad: i32,
9 9
    items: [Inner; 3],
10 10
}
11 11
12 -
static container: Container = undefined;
12 +
unsafe static container: Container = undefined;
13 13
14 -
@default fn main() -> i32 {
14 +
@default unsafe fn main() -> i32 {
15 15
    set container.items[0].value = 10;
16 16
    set container.items[1].value = 20;
17 17
    set container.items[2].value = 30;
18 18
19 19
    let slice: *mut [Inner] = &mut container.items[..];
test/tests/static.slice.offset.rad +2 -2
15 15
static SCRATCH: [Entry; 1] = [Entry { a: 0, b: 0 }];
16 16
static STORAGE: [Entry; 2] = [
17 17
    Entry { a: 0, b: 0 },
18 18
    Entry { a: 0, b: 0 },
19 19
];
20 -
static TBL: Table = undefined;
20 +
unsafe static TBL: Table = undefined;
21 21
22 -
@default fn main() -> i32 {
22 +
@default unsafe fn main() -> i32 {
23 23
    set TBL.scratch = &mut SCRATCH[..];
24 24
    set TBL.entries = &mut STORAGE[..];
25 25
    set TBL.len     = 0;
26 26
27 27
    set TBL.entries[TBL.len] = Entry { a: 7, b: 9 };
test/tests/static.zero.bss.rad +2 -2
16 16
    /// Field that forces padding after `tag`.
17 17
    value: u32,
18 18
}
19 19
20 20
/// Partially initialized aggregate where all concrete bytes are zero.
21 -
static POOL: Pool = Pool { table: undefined, count: 0 };
21 +
unsafe static POOL: Pool = Pool { table: undefined, count: 0 };
22 22
23 23
/// Repeated scalar zeros.
24 24
static FLAGS: [bool; 4] = [false; 4];
25 25
26 26
/// Explicit zero fields with undefined padding.
27 27
static PADDED: Padded = Padded { tag: 0, value: 0 };
28 28
29 -
@default fn main() -> i32 {
29 +
@default unsafe fn main() -> i32 {
30 30
    assert POOL.count == 0;
31 31
    assert FLAGS[0] == false;
32 32
    assert FLAGS[3] == false;
33 33
    assert PADDED.tag == 0;
34 34
    assert PADDED.value == 0;
test/tests/undefined.aggregate.rad +1 -1
1 1
/// Test lowering of undefined for aggregate types.
2 2
record Point: Copy { x: i32, y: i32 }
3 3
4 -
fn test() -> i32 {
4 +
unsafe fn test() -> i32 {
5 5
    let p: Point = undefined;
6 6
    return p.x;
7 7
}
test/tests/undefined.primitive.rad +1 -1
1 1
/// Test lowering of undefined for primitive types.
2 -
fn test() -> i32 {
2 +
unsafe fn test() -> i32 {
3 3
    let x: i32 = undefined;
4 4
    return x;
5 5
}
test/tests/undefined.rad +1 -1
1 1
//! returns: 0
2 2
//! Test undefined values for arrays and assignment.
3 -
@default fn main() -> i32 {
3 +
@default unsafe fn main() -> i32 {
4 4
    let mut ary: [u16; 32] = undefined;
5 5
    let x: u32 = 8;
6 6
    let y: u32 = 9;
7 7
8 8
    set ary[0] = 1;
test/tests/undefined.record.field.rad +3 -3
20 20
    len: i32,
21 21
}
22 22
23 23
/// Construct a record with one field `undefined` (named-field syntax).
24 24
/// The IL must not contain a `blit` or `store` for `x`.
25 -
fn partialInit() -> i32 {
25 +
unsafe fn partialInit() -> i32 {
26 26
    let s = Small { x: undefined, y: 42 };
27 27
    return s.y;
28 28
}
29 29
30 30
/// Construct a record with an array field `undefined` (named-field syntax).
31 31
/// The IL must not contain a `blit` for `data`.
32 -
fn arrayFieldUndef() -> i32 {
32 +
unsafe fn arrayFieldUndef() -> i32 {
33 33
    let w = WithArray { data: undefined, len: 3 };
34 34
    return w.len;
35 35
}
36 36
37 37
/// Both fields defined - normal case for comparison.
45 45
    B,
46 46
}
47 47
48 48
/// Union variant with record payload containing an `undefined` field.
49 49
/// This exercises `lowerRecordCtor` (positional constructor path).
50 -
fn unionPayloadUndef() -> i32 {
50 +
unsafe fn unionPayloadUndef() -> i32 {
51 51
    let t = Tagged::A { data: undefined, tag: 99 };
52 52
    match t {
53 53
        case Tagged::A { tag, .. } => return tag,
54 54
        else => return -1,
55 55
    }
test/tests/undefined.unsafe.rad added +33 -0
1 +
//! returns: 0
2 +
3 +
/// Permanent storage for the initialized pointers.
4 +
static DATA: [u8; 2] = [7, 9];
5 +
6 +
/// Initialize each array element before reading a pointer.
7 +
fn read() -> u8 {
8 +
    unsafe {
9 +
        let mut pointers: [*u8; 2] = undefined;
10 +
        set pointers[0] = &DATA[0];
11 +
        set pointers[1] = &DATA[1];
12 +
        return *pointers[0] + *pointers[1];
13 +
    }
14 +
}
15 +
16 +
/// Initialize a scalar before returning its value.
17 +
unsafe fn scalar() -> u8 {
18 +
    let mut value: u8 = undefined;
19 +
    set value = 4;
20 +
    return value;
21 +
}
22 +
23 +
/// Check unsafe initialization and safe empty values.
24 +
@default fn main() -> i32 {
25 +
    let bytes: [u8; 2] = [0; 2];
26 +
    let pointer: ?*u8 = nil;
27 +
    if bytes[0] <> 0 or pointer <> nil { return 1; }
28 +
    if read() <> 16 { return 2; }
29 +
    unsafe {
30 +
        if scalar() <> 4 { return 3; }
31 +
    }
32 +
    return 0;
33 +
}