lang: Make composites affine by default
702b8915c6f98686dbb3309fde047cbe8d2e83175202371578a12c861147611b
1 parent
1e2fdea9
compiler/radiance.rad
+9 -9
| 97 | 97 | /// Usage string. |
|
| 98 | 98 | constant USAGE: *[u8] = |
|
| 99 | 99 | "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> -o <output>\n"; |
|
| 100 | 100 | ||
| 101 | 101 | /// Compiler error. |
|
| 102 | - | union Error { |
|
| 102 | + | union Error: Copy { |
|
| 103 | 103 | Other, |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | 106 | /// What to dump during compilation. |
|
| 107 | - | union Dump { |
|
| 107 | + | union Dump: Copy { |
|
| 108 | 108 | /// Don't dump anything. |
|
| 109 | 109 | None, |
|
| 110 | 110 | /// Dump the parsed AST before semantic analysis. |
|
| 111 | 111 | Ast, |
|
| 112 | 112 | /// Dump the module graph. |
| 116 | 116 | /// Dump the generated assembly. |
|
| 117 | 117 | Asm, |
|
| 118 | 118 | } |
|
| 119 | 119 | ||
| 120 | 120 | /// A discovered test function. |
|
| 121 | - | record TestDesc { |
|
| 121 | + | record TestDesc: Copy { |
|
| 122 | 122 | /// Full module qualified path segments (eg. ["std", "tests"]). |
|
| 123 | 123 | modPath: *[*[u8]], |
|
| 124 | 124 | /// Test function name (eg. "testFoo"). |
|
| 125 | 125 | fnName: *[u8], |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// Source inputs belonging to one command-line package. |
|
| 129 | - | record PackageInput { |
|
| 129 | + | record PackageInput: Copy { |
|
| 130 | 130 | /// Package name from the `-pkg` argument. |
|
| 131 | 131 | name: *[u8], |
|
| 132 | 132 | /// Optional startup assembly emitted before generated text. |
|
| 133 | 133 | startupPath: ?*[u8], |
|
| 134 | 134 | /// Radiance source paths for this package. |
| 140 | 140 | /// Number of assembly source paths. |
|
| 141 | 141 | asmPathCount: u32, |
|
| 142 | 142 | } |
|
| 143 | 143 | ||
| 144 | 144 | /// Compilation context. |
|
| 145 | - | record CompileContext { |
|
| 145 | + | record CompileContext: Copy { |
|
| 146 | 146 | /// Array of packages to compile. |
|
| 147 | 147 | packages: [package::Package; MAX_PACKAGES], |
|
| 148 | 148 | /// Driver inputs for each package slot. |
|
| 149 | 149 | inputs: [PackageInput; MAX_PACKAGES], |
|
| 150 | 150 | /// Number of packages. |
| 162 | 162 | /// Whether to emit debug info (.debug file). |
|
| 163 | 163 | debug: bool, |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | 166 | /// Root module info for a package. |
|
| 167 | - | record RootModule { |
|
| 167 | + | record RootModule: Copy { |
|
| 168 | 168 | entry: *module::ModuleEntry, |
|
| 169 | 169 | ast: *mut ast::Node, |
|
| 170 | 170 | } |
|
| 171 | 171 | ||
| 172 | 172 | /// State carried by the streaming lowerer/codegen callback. |
|
| 173 | - | record CodegenSinkContext { |
|
| 173 | + | record CodegenSinkContext: Copy { |
|
| 174 | 174 | /// RV64 generator receiving lowered functions. |
|
| 175 | 175 | generator: *mut rv64::Generator, |
|
| 176 | 176 | /// Arena holding the current function's lowered IL. |
|
| 177 | 177 | fnArena: *mut alloc::Arena, |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Entry handling for streamed code generation. |
|
| 181 | - | union CodegenEntryMode { |
|
| 181 | + | union CodegenEntryMode: Copy { |
|
| 182 | 182 | /// Do not reserve an entry jump. |
|
| 183 | 183 | None, |
|
| 184 | 184 | /// Reserve and patch an entry jump to the `@default` function. |
|
| 185 | 185 | DefaultEntry, |
|
| 186 | 186 | } |
|
| 187 | 187 | ||
| 188 | 188 | /// Options controlling streamed lowering and code generation. |
|
| 189 | - | record CodegenOptions { |
|
| 189 | + | record CodegenOptions: Copy { |
|
| 190 | 190 | /// Optional output path used for progress logging. |
|
| 191 | 191 | logPath: ?*[u8], |
|
| 192 | 192 | /// Whether to emit debug source locations. |
|
| 193 | 193 | debug: bool, |
|
| 194 | 194 | /// How the generated program should handle entry. |
lib/std/arch/rv64.rad
+5 -5
| 159 | 159 | export fn imageHeader(codeBytes: u32, roDataBytes: u32, rwDataBytes: u32) -> [u32; 5] { |
|
| 160 | 160 | return [IMAGE_MAGIC, IMAGE_VERSION, codeBytes, roDataBytes, rwDataBytes]; |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | 163 | /// Storage buffers passed from driver for code generation. |
|
| 164 | - | export record Storage { |
|
| 164 | + | export record Storage: Copy { |
|
| 165 | 165 | /// Buffer for data symbols. |
|
| 166 | 166 | dataSyms: *mut [data::DataSym], |
|
| 167 | 167 | /// Hash table entries for data symbol lookup. |
|
| 168 | 168 | dataSymEntries: *mut [dict::Entry], |
|
| 169 | 169 | } |
|
| 170 | 170 | ||
| 171 | 171 | /// Result of code generation. |
|
| 172 | - | export record Program { |
|
| 172 | + | export record Program: Copy { |
|
| 173 | 173 | /// Slice of emitted code. |
|
| 174 | 174 | code: *[u32], |
|
| 175 | 175 | /// Slice of function addresses (name + start index). |
|
| 176 | 176 | funcs: *[types::FuncAddr], |
|
| 177 | 177 | /// Number of read-only data bytes emitted. |
| 181 | 181 | /// Debug entries mapping PCs to source locations. Empty when debug is off. |
|
| 182 | 182 | debugEntries: *[types::DebugEntry], |
|
| 183 | 183 | } |
|
| 184 | 184 | ||
| 185 | 185 | /// Entry jump patching requested for the generated program. |
|
| 186 | - | export union EntryPatch { |
|
| 186 | + | export union EntryPatch: Copy { |
|
| 187 | 187 | /// No entry jump is emitted. |
|
| 188 | 188 | None, |
|
| 189 | 189 | /// Reserve code slot zero for a jump to the default function. |
|
| 190 | 190 | Reserved(?*[u8]), |
|
| 191 | 191 | } |
|
| 192 | 192 | ||
| 193 | 193 | /// Options controlling incremental RV64 program generation. |
|
| 194 | - | export record ProgramOptions { |
|
| 194 | + | export record ProgramOptions: Copy { |
|
| 195 | 195 | /// Entry jump patching mode. |
|
| 196 | 196 | entryPatch: EntryPatch, |
|
| 197 | 197 | /// Whether to emit debug source locations. |
|
| 198 | 198 | debug: bool, |
|
| 199 | 199 | } |
| 201 | 201 | /// State for incremental RV64 program generation. |
|
| 202 | 202 | /// |
|
| 203 | 203 | /// The generator owns global codegen state that must survive across function |
|
| 204 | 204 | /// emission. Function-local scratch stays outside this record so callers can |
|
| 205 | 205 | /// reclaim it after each function. |
|
| 206 | - | export record Generator { |
|
| 206 | + | export record Generator: Copy { |
|
| 207 | 207 | /// Binary emitter and relocation state. |
|
| 208 | 208 | e: emit::Emitter, |
|
| 209 | 209 | /// Entry jump patching state. |
|
| 210 | 210 | entryPatch: EntryPatch, |
|
| 211 | 211 | } |
lib/std/arch/rv64/asm.rad
+15 -15
| 71 | 71 | /// consumes the program, [`rv64::addAssembly`] appends the text words to the |
|
| 72 | 72 | /// generated text stream and registers text labels at their relocated offsets. |
|
| 73 | 73 | /// The driver copies `data` into the final read-only data prefix; the data |
|
| 74 | 74 | /// base supplied to [`assemble`] lets the assembler resolve data addresses as |
|
| 75 | 75 | /// they will appear in that final layout. |
|
| 76 | - | export record Program { |
|
| 76 | + | export record Program: Copy { |
|
| 77 | 77 | /// Encoded instructions in the text section. |
|
| 78 | 78 | text: *[u32], |
|
| 79 | 79 | /// Raw bytes in the data section. |
|
| 80 | 80 | data: *[u8], |
|
| 81 | 81 | /// Symbols defined by the source. |
| 83 | 83 | /// Text references resolved by the whole-program emitter. |
|
| 84 | 84 | externalFixups: *[Fixup], |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Errors reported while assembling source text. |
|
| 88 | - | export union Error { |
|
| 88 | + | export union Error: Copy { |
|
| 89 | 89 | /// Invalid syntax or operand form at a source offset. |
|
| 90 | 90 | Invalid { offset: u32, message: *[u8] }, |
|
| 91 | 91 | /// The source emitted more text words than the caller-provided buffer holds. |
|
| 92 | 92 | TextOverflow, |
|
| 93 | 93 | /// The source emitted more data bytes than the caller-provided buffer holds. |
|
| 94 | 94 | DataOverflow, |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | 97 | /// Active output section. |
|
| 98 | - | export union Section { |
|
| 98 | + | export union Section: Copy { |
|
| 99 | 99 | /// Instruction section. |
|
| 100 | 100 | Text, |
|
| 101 | 101 | /// Data byte section. |
|
| 102 | 102 | Data, |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Branch opcode that needs fixup. |
|
| 106 | - | export union BranchOp { |
|
| 106 | + | export union BranchOp: Copy { |
|
| 107 | 107 | /// Branch if equal. |
|
| 108 | 108 | Beq, |
|
| 109 | 109 | /// Branch if not equal. |
|
| 110 | 110 | Bne, |
|
| 111 | 111 | /// Branch if less than, signed. |
| 121 | 121 | /// Branch if greater than, signed pseudo-instruction. |
|
| 122 | 122 | Bgt, |
|
| 123 | 123 | } |
|
| 124 | 124 | ||
| 125 | 125 | /// Parser and encoder behavior for one instruction mnemonic. |
|
| 126 | - | export union InstructionEncoder { |
|
| 126 | + | export union InstructionEncoder: Copy { |
|
| 127 | 127 | /// No-operand instruction encoded by a fixed encoder. |
|
| 128 | 128 | NoOperand { enc: fn() -> u32 }, |
|
| 129 | 129 | /// Load-immediate pseudo-instruction. |
|
| 130 | 130 | Li, |
|
| 131 | 131 | /// Load-address pseudo-instruction. |
| 163 | 163 | /// Upper-immediate operand form. |
|
| 164 | 164 | Upper { enc: fn(gen::Reg, i32) -> u32 }, |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | 167 | /// Classified directive name. |
|
| 168 | - | export union DirectiveKind { |
|
| 168 | + | export union DirectiveKind: Copy { |
|
| 169 | 169 | /// `.align` directive. |
|
| 170 | 170 | Align, |
|
| 171 | 171 | /// `.ascii` directive. |
|
| 172 | 172 | Ascii, |
|
| 173 | 173 | /// `.byte` directive. |
| 187 | 187 | /// `.word` directive. |
|
| 188 | 188 | Word, |
|
| 189 | 189 | } |
|
| 190 | 190 | ||
| 191 | 191 | /// Instruction descriptor table row. |
|
| 192 | - | record InstructionEntry { |
|
| 192 | + | record InstructionEntry: Copy { |
|
| 193 | 193 | /// Assembly mnemonic text. |
|
| 194 | 194 | name: *[u8], |
|
| 195 | 195 | /// Operand parser and encoder behavior. |
|
| 196 | 196 | encoder: InstructionEncoder, |
|
| 197 | 197 | } |
|
| 198 | 198 | ||
| 199 | 199 | /// Directive descriptor table row. |
|
| 200 | - | record DirectiveEntry { |
|
| 200 | + | record DirectiveEntry: Copy { |
|
| 201 | 201 | /// Directive name without the leading `.`. |
|
| 202 | 202 | name: *[u8], |
|
| 203 | 203 | /// Parser behavior for the directive. |
|
| 204 | 204 | kind: DirectiveKind, |
|
| 205 | 205 | } |
|
| 206 | 206 | ||
| 207 | 207 | /// Register descriptor table row. |
|
| 208 | - | record RegisterEntry { |
|
| 208 | + | record RegisterEntry: Copy { |
|
| 209 | 209 | /// Register alias text without the leading `%`. |
|
| 210 | 210 | name: *[u8], |
|
| 211 | 211 | /// Numeric register selected by the alias. |
|
| 212 | 212 | reg: gen::Reg, |
|
| 213 | 213 | } |
|
| 214 | 214 | ||
| 215 | 215 | /// CSR descriptor table row. |
|
| 216 | - | record CsrEntry { |
|
| 216 | + | record CsrEntry: Copy { |
|
| 217 | 217 | /// CSR name text. |
|
| 218 | 218 | name: *[u8], |
|
| 219 | 219 | /// Numeric CSR address. |
|
| 220 | 220 | csr: u32, |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | 223 | /// Width of an integer data directive. |
|
| 224 | - | export union DataWidth { |
|
| 224 | + | export union DataWidth: Copy { |
|
| 225 | 225 | /// 32-bit data value. |
|
| 226 | 226 | Word, |
|
| 227 | 227 | /// 64-bit data value. |
|
| 228 | 228 | Dword, |
|
| 229 | 229 | } |
| 416 | 416 | { name: "mtval", csr: 0x343 }, |
|
| 417 | 417 | { name: "mtvec", csr: 0x305 }, |
|
| 418 | 418 | ]; |
|
| 419 | 419 | ||
| 420 | 420 | /// Recorded symbol definition. |
|
| 421 | - | export record Symbol { |
|
| 421 | + | export record Symbol: Copy { |
|
| 422 | 422 | /// Symbol name. |
|
| 423 | 423 | name: *[u8], |
|
| 424 | 424 | /// Section the symbol belongs to. |
|
| 425 | 425 | section: Section, |
|
| 426 | 426 | /// Byte offset within the section. |
| 428 | 428 | /// Whether `.export` exported this symbol outside its assembly fragment. |
|
| 429 | 429 | isExported: bool, |
|
| 430 | 430 | } |
|
| 431 | 431 | ||
| 432 | 432 | /// Information needed to resolve a pending symbol reference. |
|
| 433 | - | export union FixupInfo { |
|
| 433 | + | export union FixupInfo: Copy { |
|
| 434 | 434 | /// Branch to a text label. |
|
| 435 | 435 | Branch { op: BranchOp, rs1: gen::Reg, rs2: gen::Reg, index: u32 }, |
|
| 436 | 436 | /// JAL-like jump to a text label. |
|
| 437 | 437 | Jal { rd: gen::Reg, index: u32 }, |
|
| 438 | 438 | /// Absolute address materialization into a register. |
| 442 | 442 | /// A 64-bit data word referring to a symbol offset. |
|
| 443 | 443 | Dword { offset: u32 }, |
|
| 444 | 444 | } |
|
| 445 | 445 | ||
| 446 | 446 | /// Pending symbol reference. |
|
| 447 | - | export record Fixup { |
|
| 447 | + | export record Fixup: Copy { |
|
| 448 | 448 | /// Referenced symbol. |
|
| 449 | 449 | symbol: *[u8], |
|
| 450 | 450 | /// Fixup payload. |
|
| 451 | 451 | info: FixupInfo, |
|
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Parser and emission state. |
|
| 455 | - | export record Assembler { |
|
| 455 | + | export record Assembler: Copy { |
|
| 456 | 456 | /// Allocation arena for temporary assembler state. |
|
| 457 | 457 | arena: *mut alloc::Arena, |
|
| 458 | 458 | /// Assembler lexical scanner. |
|
| 459 | 459 | scan: scanner::Scanner, |
|
| 460 | 460 | /// Output text buffer. |
lib/std/arch/rv64/asm/parser.rad
+1 -1
| 11 | 11 | ||
| 12 | 12 | use super::emit; |
|
| 13 | 13 | use super::scanner; |
|
| 14 | 14 | ||
| 15 | 15 | /// Parsed memory operand with base register and signed byte offset. |
|
| 16 | - | record MemOperand { |
|
| 16 | + | record MemOperand: Copy { |
|
| 17 | 17 | /// Base register inside the memory operand parentheses. |
|
| 18 | 18 | base: gen::Reg, |
|
| 19 | 19 | /// Signed byte offset preceding the base register. |
|
| 20 | 20 | offset: i32, |
|
| 21 | 21 | } |
lib/std/arch/rv64/asm/scanner.rad
+4 -4
| 3 | 3 | ||
| 4 | 4 | use std::char; |
|
| 5 | 5 | use std::lang::strings; |
|
| 6 | 6 | ||
| 7 | 7 | /// Token kinds recognized by the assembler scanner. |
|
| 8 | - | export union TokenKind { |
|
| 8 | + | export union TokenKind: Copy { |
|
| 9 | 9 | /// Special end-of-file token generated when the input is exhausted. |
|
| 10 | 10 | Eof, |
|
| 11 | 11 | /// Special invalid token carrying an error message in [`Token::source`]. |
|
| 12 | 12 | Invalid, |
|
| 13 | 13 |
| 40 | 40 | /// Integer literal token. |
|
| 41 | 41 | Number, |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Describes where assembler source originated from. |
|
| 45 | - | export union SourceKind { |
|
| 45 | + | export union SourceKind: Copy { |
|
| 46 | 46 | /// Source loaded from a file at the given path. |
|
| 47 | 47 | File { path: *[u8] }, |
|
| 48 | 48 | /// Source provided as an inline string. |
|
| 49 | 49 | String, |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Lexical scanner state for assembler source. |
|
| 53 | - | export record Scanner { |
|
| 53 | + | export record Scanner: Copy { |
|
| 54 | 54 | /// Origin of the source being scanned. |
|
| 55 | 55 | sourceKind: SourceKind, |
|
| 56 | 56 | /// Source buffer. |
|
| 57 | 57 | source: *[u8], |
|
| 58 | 58 | /// Offset of the current token in `source`. |
| 66 | 66 | /// Intern pool for identifier-shaped token text. |
|
| 67 | 67 | pool: *mut strings::Pool, |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Individual token with kind, source text, and byte offset. |
|
| 71 | - | export record Token { |
|
| 71 | + | export record Token: Copy { |
|
| 72 | 72 | /// Token kind. |
|
| 73 | 73 | kind: TokenKind, |
|
| 74 | 74 | /// Token source text. |
|
| 75 | 75 | source: *[u8], |
|
| 76 | 76 | /// Byte offset of `source` in the input buffer. |
lib/std/arch/rv64/decode.rad
+1 -1
| 101 | 101 | ///////////////////////// |
|
| 102 | 102 | // Decoded Instruction // |
|
| 103 | 103 | ///////////////////////// |
|
| 104 | 104 | ||
| 105 | 105 | /// Decoded RV64 instruction. |
|
| 106 | - | export union Instr { |
|
| 106 | + | export union Instr: Copy { |
|
| 107 | 107 | // R-type ALU. |
|
| 108 | 108 | Add { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 109 | 109 | Sub { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 110 | 110 | Sll { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 111 | 111 | Slt { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
lib/std/arch/rv64/emit.rad
+10 -10
| 29 | 29 | ////////////////////// |
|
| 30 | 30 | // Emission Context // |
|
| 31 | 31 | ////////////////////// |
|
| 32 | 32 | ||
| 33 | 33 | /// Branch/jump that needs offset patching after all blocks are emitted. |
|
| 34 | - | export record PendingBranch { |
|
| 34 | + | export record PendingBranch: Copy { |
|
| 35 | 35 | /// Index into code buffer where the branch instruction is. |
|
| 36 | 36 | index: u32, |
|
| 37 | 37 | /// Target block index. |
|
| 38 | 38 | target: u32, |
|
| 39 | 39 | /// Type of branch for re-encoding. |
|
| 40 | 40 | kind: BranchKind, |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Type of branch instruction. |
|
| 44 | - | export union BranchKind { |
|
| 44 | + | export union BranchKind: Copy { |
|
| 45 | 45 | /// Conditional branch (B-type encoding). |
|
| 46 | 46 | Cond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 47 | 47 | /// Inverted conditional branch (B-type encoding with negated condition). |
|
| 48 | 48 | InvertedCond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 49 | 49 | /// Unconditional jump (J-type encoding). |
|
| 50 | 50 | Jump, |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// Function call that needs offset patching. |
|
| 54 | - | export record PendingCall { |
|
| 54 | + | export record PendingCall: Copy { |
|
| 55 | 55 | /// Index in code buffer where the call was emitted. |
|
| 56 | 56 | index: u32, |
|
| 57 | 57 | /// Target function name. |
|
| 58 | 58 | target: *[u8], |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Assembly jump that needs offset patching after all text is emitted. |
|
| 62 | - | export record PendingJump { |
|
| 62 | + | export record PendingJump: Copy { |
|
| 63 | 63 | /// Index in code buffer where the jump was emitted. |
|
| 64 | 64 | index: u32, |
|
| 65 | 65 | /// Target function name. |
|
| 66 | 66 | target: *[u8], |
|
| 67 | 67 | /// Destination register. |
|
| 68 | 68 | rd: gen::Reg, |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// Address load that needs patching after layout is known. |
|
| 72 | - | export record PendingAddrLoad { |
|
| 72 | + | export record PendingAddrLoad: Copy { |
|
| 73 | 73 | /// Index in code buffer where the load was emitted. |
|
| 74 | 74 | index: u32, |
|
| 75 | 75 | /// Target function or data symbol name. |
|
| 76 | 76 | target: *[u8], |
|
| 77 | 77 | /// Destination register. |
| 79 | 79 | /// Whether this is an absolute data symbol address. |
|
| 80 | 80 | isData: bool, |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | 83 | /// Adjusted base register and offset for addressing. |
|
| 84 | - | export record AdjustedOffset { |
|
| 84 | + | export record AdjustedOffset: Copy { |
|
| 85 | 85 | /// Base register. |
|
| 86 | 86 | base: gen::Reg, |
|
| 87 | 87 | /// Byte offset from register. |
|
| 88 | 88 | offset: i32, |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | 91 | /// Callee-saved register with its stack offset. |
|
| 92 | - | export record SavedReg { |
|
| 92 | + | export record SavedReg: Copy { |
|
| 93 | 93 | /// Register to save/restore. |
|
| 94 | 94 | reg: gen::Reg, |
|
| 95 | 95 | /// Offset from SP. |
|
| 96 | 96 | offset: i32, |
|
| 97 | 97 | } |
|
| 98 | 98 | ||
| 99 | 99 | /// Emission context. Tracks state during code generation. |
|
| 100 | - | export record Emitter { |
|
| 100 | + | export record Emitter: Copy { |
|
| 101 | 101 | /// Allocator for growing append-backed emitter lists. |
|
| 102 | 102 | allocator: alloc::Allocator, |
|
| 103 | 103 | /// Emitted instructions storage. |
|
| 104 | 104 | code: *mut [u32], |
|
| 105 | 105 | /// Current number of emitted instructions. |
| 121 | 121 | /// Number of debug entries recorded. |
|
| 122 | 122 | debugEntriesLen: u32, |
|
| 123 | 123 | } |
|
| 124 | 124 | ||
| 125 | 125 | /// Computed stack frame layout for a function. |
|
| 126 | - | export record Frame { |
|
| 126 | + | export record Frame: Copy { |
|
| 127 | 127 | /// Total frame size in bytes (aligned). |
|
| 128 | 128 | totalSize: i32, |
|
| 129 | 129 | /// Callee-saved registers and their offsets. |
|
| 130 | 130 | // TODO: Use constant length when language supports it. |
|
| 131 | 131 | savedRegs: [SavedReg; super::NUM_SAVED_REGISTERS], |
| 453 | 453 | ///////////////////////// |
|
| 454 | 454 | // Immediate Handling // |
|
| 455 | 455 | ///////////////////////// |
|
| 456 | 456 | ||
| 457 | 457 | /// Split immediate into `hi` and `lo` bits. |
|
| 458 | - | export record SplitImm { |
|
| 458 | + | export record SplitImm: Copy { |
|
| 459 | 459 | /// Upper 20 bits. |
|
| 460 | 460 | hi: i32, |
|
| 461 | 461 | /// Lower 12 bits. |
|
| 462 | 462 | lo: i32, |
|
| 463 | 463 | } |
lib/std/arch/rv64/isel.rad
+6 -6
| 63 | 63 | constant U8_MAX: i64 = 255; |
|
| 64 | 64 | constant U16_MAX: i64 = 65535; |
|
| 65 | 65 | constant U32_MAX: i64 = 4294967295; |
|
| 66 | 66 | ||
| 67 | 67 | /// Binary operation. |
|
| 68 | - | union BinOp { Add, And, Or, Xor } |
|
| 68 | + | union BinOp: Copy { Add, And, Or, Xor } |
|
| 69 | 69 | /// Shift operation. |
|
| 70 | - | union ShiftOp { Sll, Srl, Sra } |
|
| 70 | + | union ShiftOp: Copy { Sll, Srl, Sra } |
|
| 71 | 71 | /// Compare operation. |
|
| 72 | - | union CmpOp { Slt, Ult } |
|
| 72 | + | union CmpOp: Copy { Slt, Ult } |
|
| 73 | 73 | ||
| 74 | 74 | /// A pending spill store to be flushed after instruction selection. |
|
| 75 | - | record PendingSpill { |
|
| 75 | + | record PendingSpill: Copy { |
|
| 76 | 76 | /// The SSA register that was spilled. |
|
| 77 | 77 | ssa: il::Reg, |
|
| 78 | 78 | /// The physical register holding the value to store. |
|
| 79 | 79 | rd: gen::Reg, |
|
| 80 | 80 | } |
| 82 | 82 | //////////////////// |
|
| 83 | 83 | // Selector State // |
|
| 84 | 84 | //////////////////// |
|
| 85 | 85 | ||
| 86 | 86 | /// Instruction selector state. |
|
| 87 | - | export record Selector { |
|
| 87 | + | export record Selector: Copy { |
|
| 88 | 88 | /// Emitter for outputting instructions. |
|
| 89 | 89 | e: *mut emit::Emitter, |
|
| 90 | 90 | /// Register allocation result. |
|
| 91 | 91 | ralloc: *regalloc::AllocResult, |
|
| 92 | 92 | /// Total stack frame size. |
| 263 | 263 | //////////////////////// |
|
| 264 | 264 | // Instruction Select // |
|
| 265 | 265 | //////////////////////// |
|
| 266 | 266 | ||
| 267 | 267 | /// Pre-scan result for reserve analysis. |
|
| 268 | - | record ReserveInfo { |
|
| 268 | + | record ReserveInfo: Copy { |
|
| 269 | 269 | /// Total size needed for constant-sized reserves. |
|
| 270 | 270 | size: i32, |
|
| 271 | 271 | /// Whether any dynamic-sized reserves exist. |
|
| 272 | 272 | isDynamic: bool, |
|
| 273 | 273 | } |
lib/std/collections/dict.rad
+2 -2
| 4 | 4 | //! of two and is provided by the caller via arena-allocated storage. |
|
| 5 | 5 | ||
| 6 | 6 | use std::mem; |
|
| 7 | 7 | ||
| 8 | 8 | /// Hash map entry. |
|
| 9 | - | export record Entry { |
|
| 9 | + | export record Entry: Copy { |
|
| 10 | 10 | /// Key. |
|
| 11 | 11 | key: *[u8], |
|
| 12 | 12 | /// Associated value. |
|
| 13 | 13 | value: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Open-addressed hash map with caller-provided storage. |
|
| 17 | - | export record Dict { |
|
| 17 | + | export record Dict: Copy { |
|
| 18 | 18 | /// Hash table entries. |
|
| 19 | 19 | entries: *mut [Entry], |
|
| 20 | 20 | /// Number of occupied entries. |
|
| 21 | 21 | count: u32, |
|
| 22 | 22 | } |
lib/std/fmt.rad
+3 -3
| 13 | 13 | export constant I64_STR_LEN: u32 = 20; |
|
| 14 | 14 | /// Maximum string length for a formatted bool (eg. "false"). |
|
| 15 | 15 | export constant BOOL_STR_LEN: u32 = 5; |
|
| 16 | 16 | ||
| 17 | 17 | /// Radix/base of a parsed integer literal. |
|
| 18 | - | export union Radix { |
|
| 18 | + | export union Radix: Copy { |
|
| 19 | 19 | /// Binary literal (0b...). |
|
| 20 | 20 | Binary, |
|
| 21 | 21 | /// Decimal literal. |
|
| 22 | 22 | Decimal, |
|
| 23 | 23 | /// Hexadecimal literal (0x...). |
|
| 24 | 24 | Hex, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Errors reported while parsing literal text. |
|
| 28 | - | export union ParseError { |
|
| 28 | + | export union ParseError: Copy { |
|
| 29 | 29 | /// Literal text was empty or missing required digits. |
|
| 30 | 30 | Invalid, |
|
| 31 | 31 | /// Literal contained an invalid digit for its radix. |
|
| 32 | 32 | InvalidDigit, |
|
| 33 | 33 | /// Literal value exceeded the supported range. |
|
| 34 | 34 | Overflow, |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Parsed integer literal metadata. |
|
| 38 | - | export record IntLiteral { |
|
| 38 | + | export record IntLiteral: Copy { |
|
| 39 | 39 | /// Raw characters that comprised the literal. |
|
| 40 | 40 | text: *[u8], |
|
| 41 | 41 | /// Magnitude parsed from the literal. |
|
| 42 | 42 | magnitude: u64, |
|
| 43 | 43 | /// Radix used by the literal. |
lib/std/lang/alloc.rad
+3 -3
| 7 | 7 | @test mod tests; |
|
| 8 | 8 | ||
| 9 | 9 | use std::mem; |
|
| 10 | 10 | ||
| 11 | 11 | /// Error thrown by allocator. |
|
| 12 | - | export union AllocError { |
|
| 12 | + | export union AllocError: Copy { |
|
| 13 | 13 | /// Allocator is out of memory. |
|
| 14 | 14 | OutOfMemory, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Bump allocator backed by a byte slice. |
|
| 18 | 18 | /// |
|
| 19 | 19 | /// Allocations are made by advancing an offset pointer. Individual allocations |
|
| 20 | 20 | /// cannot be freed; instead, the entire arena is reset at once via [`reset`]. |
|
| 21 | - | export record Arena { |
|
| 21 | + | export record Arena: Copy { |
|
| 22 | 22 | /// Backing storage. |
|
| 23 | 23 | data: *mut [u8], |
|
| 24 | 24 | /// Current allocation offset in bytes. |
|
| 25 | 25 | offset: u32, |
|
| 26 | 26 | } |
| 109 | 109 | /// Bundles an allocation function with an opaque context pointer so that |
|
| 110 | 110 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
|
| 111 | 111 | /// through a uniform interface. The `func` field is called with the context |
|
| 112 | 112 | /// pointer, a byte size and an alignment, and must return a pointer to |
|
| 113 | 113 | /// the allocated memory or panic on failure. |
|
| 114 | - | export record Allocator { |
|
| 114 | + | export record Allocator: Copy { |
|
| 115 | 115 | /// Allocation function. Returns a pointer to `size` bytes |
|
| 116 | 116 | /// aligned to `alignment`, or panics on failure. |
|
| 117 | 117 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 118 | 118 | /// Opaque context pointer passed to `func`. |
|
| 119 | 119 | ctx: *mut opaque, |
lib/std/lang/ast.rad
+49 -49
| 10 | 10 | export constant MAX_TRAIT_METHODS: u32 = 8; |
|
| 11 | 11 | ||
| 12 | 12 | /// Arena for all parser allocations. |
|
| 13 | 13 | /// |
|
| 14 | 14 | /// Uses a bump allocator for both AST nodes and node pointer arrays. |
|
| 15 | - | export record NodeArena { |
|
| 15 | + | export record NodeArena: Copy { |
|
| 16 | 16 | /// Bump allocator for all allocations. |
|
| 17 | 17 | arena: alloc::Arena, |
|
| 18 | 18 | /// Next node ID to assign. Incremented on each node allocation. |
|
| 19 | 19 | nextId: u32, |
|
| 20 | 20 | } |
| 36 | 36 | ||
| 37 | 37 | return @sliceOf(ptr.ptr as *mut *Node, 0, capacity); |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Attribute bit set applied to declarations or fields. |
|
| 41 | - | export union Attribute { |
|
| 41 | + | export union Attribute: Copy { |
|
| 42 | 42 | /// Public visibility attribute. |
|
| 43 | 43 | Export = 0b1, |
|
| 44 | 44 | /// Default implementation attribute. |
|
| 45 | 45 | Default = 0b10, |
|
| 46 | 46 | /// Extern linkage attribute. |
| 52 | 52 | /// Declaration may perform unsafe pointer operations. |
|
| 53 | 53 | Unsafe = 0b100000, |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Ordered collection of attribute nodes applied to a declaration. |
|
| 57 | - | export record Attributes { |
|
| 57 | + | export record Attributes: Copy { |
|
| 58 | 58 | list: *mut [*Node], |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Check if an attributes list contains an attribute. |
|
| 62 | 62 | export fn attributesContains(self: *Attributes, attr: Attribute) -> bool { |
| 72 | 72 | export fn hasAttribute(attrs: u32, attr: Attribute) -> bool { |
|
| 73 | 73 | return (attrs & (attr as u32)) <> 0; |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Signedness of an integer type. |
|
| 77 | - | export union Signedness { |
|
| 77 | + | export union Signedness: Copy { |
|
| 78 | 78 | /// Signed, eg. `i8`. |
|
| 79 | 79 | Signed, |
|
| 80 | 80 | /// Unsigned, eg. `u32`. |
|
| 81 | 81 | Unsigned, |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | 84 | /// Binary operator kinds used in numeric expressions. |
|
| 85 | - | export union BinaryOp { |
|
| 85 | + | export union BinaryOp: Copy { |
|
| 86 | 86 | /// Addition (`+`). |
|
| 87 | 87 | Add, |
|
| 88 | 88 | /// Subtraction (`-`). |
|
| 89 | 89 | Sub, |
|
| 90 | 90 | /// Multiplication (`*`). |
| 124 | 124 | /// Logical exclusive disjunction (`xor`). |
|
| 125 | 125 | Xor, |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// Unary operator kinds used in expressions. |
|
| 129 | - | export union UnaryOp { |
|
| 129 | + | export union UnaryOp: Copy { |
|
| 130 | 130 | /// Logical negation (`not`). |
|
| 131 | 131 | Not, |
|
| 132 | 132 | /// Arithmetic negation (`-`). |
|
| 133 | 133 | Neg, |
|
| 134 | 134 | /// Bitwise NOT (`~`). |
|
| 135 | 135 | BitNot, |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | 138 | /// Builtin function kind. |
|
| 139 | - | export union Builtin { |
|
| 139 | + | export union Builtin: Copy { |
|
| 140 | 140 | /// Size of type in bytes (`@sizeOf`). |
|
| 141 | 141 | SizeOf, |
|
| 142 | 142 | /// Alignment requirement of type (`@alignOf`). |
|
| 143 | 143 | AlignOf, |
|
| 144 | 144 | /// Construct a slice from pointer, length, and optional capacity (`@sliceOf`). |
|
| 145 | 145 | SliceOf, |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Source extent for a node measured in bytes. |
|
| 149 | - | export record Span { |
|
| 149 | + | export record Span: Copy { |
|
| 150 | 150 | /// Byte offset from the start of the source file. |
|
| 151 | 151 | offset: u32, |
|
| 152 | 152 | /// Length of the node in bytes. |
|
| 153 | 153 | length: u32, |
|
| 154 | 154 | } |
|
| 155 | 155 | ||
| 156 | 156 | /// Type signature node. |
|
| 157 | - | export union TypeSig { |
|
| 157 | + | export union TypeSig: Copy { |
|
| 158 | 158 | /// Absence of type. |
|
| 159 | 159 | Void, |
|
| 160 | 160 | /// Opaque type. |
|
| 161 | 161 | Opaque, |
|
| 162 | 162 | /// Boolean type. |
| 220 | 220 | mutable: bool, |
|
| 221 | 221 | }, |
|
| 222 | 222 | } |
|
| 223 | 223 | ||
| 224 | 224 | /// Function signature. |
|
| 225 | - | export record FnSig { |
|
| 225 | + | export record FnSig: Copy { |
|
| 226 | 226 | /// Parameter type nodes in declaration order. |
|
| 227 | 227 | params: *mut [*Node], |
|
| 228 | 228 | /// Optional return type node. |
|
| 229 | 229 | returnType: ?*Node, |
|
| 230 | 230 | /// Throwable type nodes declared in the signature. |
|
| 231 | 231 | throwList: *mut [*Node], |
|
| 232 | 232 | } |
|
| 233 | 233 | ||
| 234 | 234 | /// Address-of expression metadata. |
|
| 235 | - | export record AddressOf { |
|
| 235 | + | export record AddressOf: Copy { |
|
| 236 | 236 | /// Target expression being referenced. |
|
| 237 | 237 | target: *Node, |
|
| 238 | 238 | /// Indicates whether the reference is mutable. |
|
| 239 | 239 | mutable: bool, |
|
| 240 | 240 | } |
|
| 241 | 241 | ||
| 242 | 242 | /// Compound statement block with optional dedicated scope. |
|
| 243 | - | export record Block { |
|
| 243 | + | export record Block: Copy { |
|
| 244 | 244 | /// Statements that belong to this block. |
|
| 245 | 245 | statements: *mut [*Node], |
|
| 246 | 246 | } |
|
| 247 | 247 | ||
| 248 | 248 | /// Function call expression. |
|
| 249 | - | export record Call { |
|
| 249 | + | export record Call: Copy { |
|
| 250 | 250 | /// Callee expression. |
|
| 251 | 251 | callee: *Node, |
|
| 252 | 252 | /// Argument expressions in source order. |
|
| 253 | 253 | args: *mut [*Node], |
|
| 254 | 254 | } |
|
| 255 | 255 | ||
| 256 | 256 | /// Single argument to a function or record literal, optionally labeled. |
|
| 257 | - | export record Arg { |
|
| 257 | + | export record Arg: Copy { |
|
| 258 | 258 | /// Optional label applied to the argument. |
|
| 259 | 259 | label: ?*Node, |
|
| 260 | 260 | /// Expression supplying the argument value. |
|
| 261 | 261 | value: *Node, |
|
| 262 | 262 | } |
|
| 263 | 263 | ||
| 264 | 264 | /// Assignment expression connecting a target and value. |
|
| 265 | - | export record Assign { |
|
| 265 | + | export record Assign: Copy { |
|
| 266 | 266 | /// Expression representing the assignment target. |
|
| 267 | 267 | left: *Node, |
|
| 268 | 268 | /// Expression providing the value being assigned. |
|
| 269 | 269 | right: *Node, |
|
| 270 | 270 | } |
|
| 271 | 271 | ||
| 272 | 272 | /// While loop with an optional alternate branch. |
|
| 273 | - | export record While { |
|
| 273 | + | export record While: Copy { |
|
| 274 | 274 | /// Condition evaluated before each iteration. |
|
| 275 | 275 | condition: *Node, |
|
| 276 | 276 | /// Loop body executed while `condition` is true. |
|
| 277 | 277 | body: *Node, |
|
| 278 | 278 | /// Optional branch executed when the condition is false at entry. |
|
| 279 | 279 | elseBranch: ?*Node, |
|
| 280 | 280 | } |
|
| 281 | 281 | ||
| 282 | 282 | /// `while let` loop binding metadata. |
|
| 283 | - | export record WhileLet { |
|
| 283 | + | export record WhileLet: Copy { |
|
| 284 | 284 | /// Pattern matching structure. |
|
| 285 | 285 | pattern: PatternMatch, |
|
| 286 | 286 | /// Loop body executed when the pattern matches. |
|
| 287 | 287 | body: *Node, |
|
| 288 | 288 | /// Optional branch executed when the match fails immediately. |
|
| 289 | 289 | elseBranch: ?*Node, |
|
| 290 | 290 | } |
|
| 291 | 291 | ||
| 292 | 292 | /// Try expression metadata. |
|
| 293 | - | export record Try { |
|
| 293 | + | export record Try: Copy { |
|
| 294 | 294 | /// Expression evaluated with implicit error propagation. |
|
| 295 | 295 | expr: *Node, |
|
| 296 | 296 | /// Catch clauses. Empty for propagation (`try`), `try!`, or `try?`. |
|
| 297 | 297 | catches: *mut [*Node], |
|
| 298 | 298 | /// Whether the try should panic instead of returning an error. |
| 300 | 300 | /// Whether the try should return an optional instead of propagating error. |
|
| 301 | 301 | returnsOptional: bool, |
|
| 302 | 302 | } |
|
| 303 | 303 | ||
| 304 | 304 | /// A single catch clause in a `try ... catch` expression. |
|
| 305 | - | export record CatchClause { |
|
| 305 | + | export record CatchClause: Copy { |
|
| 306 | 306 | /// Optional identifier binding for the error value (eg. `e`). |
|
| 307 | 307 | binding: ?*Node, |
|
| 308 | 308 | /// Optional type annotation after `as` (eg. `IoError`). |
|
| 309 | 309 | typeNode: ?*Node, |
|
| 310 | 310 | /// Block body executed when this clause matches. |
|
| 311 | 311 | body: *Node, |
|
| 312 | 312 | } |
|
| 313 | 313 | ||
| 314 | 314 | /// `for` loop metadata. |
|
| 315 | - | export record For { |
|
| 315 | + | export record For: Copy { |
|
| 316 | 316 | /// Loop variable binding. |
|
| 317 | 317 | binding: *Node, |
|
| 318 | 318 | /// Optional index binding for enumeration loops. |
|
| 319 | 319 | index: ?*Node, |
|
| 320 | 320 | /// Expression producing the iterable value. |
| 324 | 324 | /// Optional branch executed when the loop body never runs. |
|
| 325 | 325 | elseBranch: ?*Node, |
|
| 326 | 326 | } |
|
| 327 | 327 | ||
| 328 | 328 | /// Conditional `if` statement metadata. |
|
| 329 | - | export record If { |
|
| 329 | + | export record If: Copy { |
|
| 330 | 330 | /// Condition controlling the branch. |
|
| 331 | 331 | condition: *Node, |
|
| 332 | 332 | /// Branch executed when `condition` is true. |
|
| 333 | 333 | thenBranch: *Node, |
|
| 334 | 334 | /// Optional branch executed when `condition` is false. |
|
| 335 | 335 | elseBranch: ?*Node, |
|
| 336 | 336 | } |
|
| 337 | 337 | ||
| 338 | 338 | /// Conditional expression (`<true> if <condition> else <false>`). |
|
| 339 | - | export record CondExpr { |
|
| 339 | + | export record CondExpr: Copy { |
|
| 340 | 340 | /// Condition controlling which branch is evaluated. |
|
| 341 | 341 | condition: *Node, |
|
| 342 | 342 | /// Expression evaluated when `condition` is true. |
|
| 343 | 343 | thenExpr: *Node, |
|
| 344 | 344 | /// Expression evaluated when `condition` is false. |
|
| 345 | 345 | elseExpr: *Node, |
|
| 346 | 346 | } |
|
| 347 | 347 | ||
| 348 | 348 | /// Classification of pattern matches (if-let, while-let, let-else). |
|
| 349 | - | export union PatternKind { |
|
| 349 | + | export union PatternKind: Copy { |
|
| 350 | 350 | /// Case pattern match. |
|
| 351 | 351 | Case, |
|
| 352 | 352 | /// Binding pattern match. |
|
| 353 | 353 | Binding, |
|
| 354 | 354 | } |
|
| 355 | 355 | ||
| 356 | 356 | /// Prong arm. |
|
| 357 | - | export union ProngArm { |
|
| 357 | + | export union ProngArm: Copy { |
|
| 358 | 358 | /// Case arm with pattern list. |
|
| 359 | 359 | Case(*mut [*Node]), |
|
| 360 | 360 | /// Binding arm with single identifier or placeholder. |
|
| 361 | 361 | Binding(*Node), |
|
| 362 | 362 | /// Else arm. |
|
| 363 | 363 | Else, |
|
| 364 | 364 | } |
|
| 365 | 365 | ||
| 366 | 366 | /// Common pattern matching structure used by `if let`, `while let`, and `let-else`. |
|
| 367 | - | export record PatternMatch { |
|
| 367 | + | export record PatternMatch: Copy { |
|
| 368 | 368 | /// Pattern or binding to match against. |
|
| 369 | 369 | pattern: *Node, |
|
| 370 | 370 | /// Scrutinee expression to match against. |
|
| 371 | 371 | scrutinee: *Node, |
|
| 372 | 372 | /// Optional guard that must evaluate to `true`. |
| 376 | 376 | /// Whether the binding is mutable. |
|
| 377 | 377 | mutable: bool, |
|
| 378 | 378 | } |
|
| 379 | 379 | ||
| 380 | 380 | /// `if let` conditional binding metadata. |
|
| 381 | - | export record IfLet { |
|
| 381 | + | export record IfLet: Copy { |
|
| 382 | 382 | /// Pattern matching structure. |
|
| 383 | 383 | pattern: PatternMatch, |
|
| 384 | 384 | /// Branch executed when the pattern matches. |
|
| 385 | 385 | thenBranch: *Node, |
|
| 386 | 386 | /// Optional branch executed when the match fails. |
|
| 387 | 387 | elseBranch: ?*Node, |
|
| 388 | 388 | } |
|
| 389 | 389 | ||
| 390 | 390 | /// `let-else` statement metadata. |
|
| 391 | - | export record LetElse { |
|
| 391 | + | export record LetElse: Copy { |
|
| 392 | 392 | /// Pattern matching structure. |
|
| 393 | 393 | pattern: PatternMatch, |
|
| 394 | 394 | /// Else branch executed if match fails (must diverge). |
|
| 395 | 395 | elseBranch: *Node, |
|
| 396 | 396 | } |
|
| 397 | 397 | ||
| 398 | 398 | /// `match` statement metadata. |
|
| 399 | - | export record Match { |
|
| 399 | + | export record Match: Copy { |
|
| 400 | 400 | /// Expression whose value controls the match. |
|
| 401 | 401 | subject: *Node, |
|
| 402 | 402 | /// Prong nodes evaluated in order. |
|
| 403 | 403 | prongs: *mut [*Node], |
|
| 404 | 404 | } |
|
| 405 | 405 | ||
| 406 | 406 | /// `match` prong metadata. |
|
| 407 | - | export record MatchProng { |
|
| 407 | + | export record MatchProng: Copy { |
|
| 408 | 408 | /// Prong arm. |
|
| 409 | 409 | arm: ProngArm, |
|
| 410 | 410 | /// Optional guard that must evaluate to `true`. |
|
| 411 | 411 | guard: ?*Node, |
|
| 412 | 412 | /// Body executed when patterns match and guard passes. |
|
| 413 | 413 | body: *Node, |
|
| 414 | 414 | } |
|
| 415 | 415 | ||
| 416 | 416 | /// `let` binding. |
|
| 417 | - | export record Let { |
|
| 417 | + | export record Let: Copy { |
|
| 418 | 418 | /// Identifier bound by the declaration. |
|
| 419 | 419 | ident: *Node, |
|
| 420 | 420 | /// Declared type annotation. |
|
| 421 | 421 | type: ?*Node, |
|
| 422 | 422 | /// Initializer expression. |
| 426 | 426 | /// Whether the variable is mutable. |
|
| 427 | 427 | mutable: bool, |
|
| 428 | 428 | } |
|
| 429 | 429 | ||
| 430 | 430 | /// Constant declaration. |
|
| 431 | - | export record ConstDecl { |
|
| 431 | + | export record ConstDecl: Copy { |
|
| 432 | 432 | /// Identifier bound by the declaration. |
|
| 433 | 433 | ident: *Node, |
|
| 434 | 434 | /// Declared type annotation. |
|
| 435 | 435 | type: *Node, |
|
| 436 | 436 | /// Constant initializer expression. |
| 438 | 438 | /// Optional attribute list applied to the constant. |
|
| 439 | 439 | attrs: ?Attributes, |
|
| 440 | 440 | } |
|
| 441 | 441 | ||
| 442 | 442 | /// Static storage declaration. |
|
| 443 | - | export record StaticDecl { |
|
| 443 | + | export record StaticDecl: Copy { |
|
| 444 | 444 | /// Identifier bound by the declaration. |
|
| 445 | 445 | ident: *Node, |
|
| 446 | 446 | /// Declared storage type. |
|
| 447 | 447 | type: *Node, |
|
| 448 | 448 | /// Initialization expression. |
| 450 | 450 | /// Optional attribute list applied to the static. |
|
| 451 | 451 | attrs: ?Attributes, |
|
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Function parameter declaration. |
|
| 455 | - | export record FnParam { |
|
| 455 | + | export record FnParam: Copy { |
|
| 456 | 456 | /// Parameter identifier. |
|
| 457 | 457 | name: *Node, |
|
| 458 | 458 | /// Parameter type annotation. |
|
| 459 | 459 | type: *Node, |
|
| 460 | 460 | } |
|
| 461 | 461 | ||
| 462 | 462 | /// Record literal expression metadata. |
|
| 463 | - | export record RecordLit { |
|
| 463 | + | export record RecordLit: Copy { |
|
| 464 | 464 | /// Type name associated with the literal. |
|
| 465 | 465 | /// If `nil`, it's an anonymous record literal. |
|
| 466 | 466 | typeName: ?*Node, |
|
| 467 | 467 | /// Field initializer nodes. |
|
| 468 | 468 | fields: *mut [*Node], |
|
| 469 | 469 | /// When true, remaining fields are discarded (`{ x, .. }`). |
|
| 470 | 470 | ignoreRest: bool, |
|
| 471 | 471 | } |
|
| 472 | 472 | ||
| 473 | 473 | /// Record declaration. |
|
| 474 | - | export record RecordDecl { |
|
| 474 | + | export record RecordDecl: Copy { |
|
| 475 | 475 | /// Identifier naming the record. |
|
| 476 | 476 | name: *Node, |
|
| 477 | 477 | /// Field declaration nodes. |
|
| 478 | 478 | fields: *mut [*Node], |
|
| 479 | 479 | /// Optional attribute list applied to the record. |
| 483 | 483 | /// Whether this record has labeled fields. |
|
| 484 | 484 | labeled: bool, |
|
| 485 | 485 | } |
|
| 486 | 486 | ||
| 487 | 487 | /// Union declarations. |
|
| 488 | - | export record UnionDecl { |
|
| 488 | + | export record UnionDecl: Copy { |
|
| 489 | 489 | /// Identifier naming the union. |
|
| 490 | 490 | name: *Node, |
|
| 491 | 491 | /// Variant nodes making up the union. |
|
| 492 | 492 | variants: *mut [*Node], |
|
| 493 | 493 | /// Optional attribute list applied to the union. |
| 495 | 495 | /// Trait derivations attached to the union. |
|
| 496 | 496 | derives: *mut [*Node], |
|
| 497 | 497 | } |
|
| 498 | 498 | ||
| 499 | 499 | /// Union variant declaration. |
|
| 500 | - | export record UnionDeclVariant { |
|
| 500 | + | export record UnionDeclVariant: Copy { |
|
| 501 | 501 | /// Identifier naming the variant. |
|
| 502 | 502 | name: *Node, |
|
| 503 | 503 | /// Variant index. |
|
| 504 | 504 | index: u32, |
|
| 505 | 505 | /// Explicit discriminant value, if provided. |
| 507 | 507 | /// Optional payload type. |
|
| 508 | 508 | type: ?*Node, |
|
| 509 | 509 | } |
|
| 510 | 510 | ||
| 511 | 511 | /// Function declaration. |
|
| 512 | - | export record FnDecl { |
|
| 512 | + | export record FnDecl: Copy { |
|
| 513 | 513 | /// Identifier naming the function. |
|
| 514 | 514 | name: *Node, |
|
| 515 | 515 | /// Function type signature. |
|
| 516 | 516 | sig: FnSig, |
|
| 517 | 517 | /// Optional function body (`nil` for extern functions). |
| 519 | 519 | /// Optional attribute list applied to the function. |
|
| 520 | 520 | attrs: ?Attributes, |
|
| 521 | 521 | } |
|
| 522 | 522 | ||
| 523 | 523 | /// Array repeat literal metadata. |
|
| 524 | - | export record ArrayRepeatLit { |
|
| 524 | + | export record ArrayRepeatLit: Copy { |
|
| 525 | 525 | /// Expression providing the repeated value. |
|
| 526 | 526 | item: *Node, |
|
| 527 | 527 | /// Expression providing the repetition count. |
|
| 528 | 528 | count: *Node, |
|
| 529 | 529 | } |
|
| 530 | 530 | ||
| 531 | 531 | /// Module declaration. |
|
| 532 | - | export record Mod { |
|
| 532 | + | export record Mod: Copy { |
|
| 533 | 533 | /// Identifier naming the module. |
|
| 534 | 534 | name: *Node, |
|
| 535 | 535 | /// Optional attribute list applied to the module. |
|
| 536 | 536 | attrs: ?Attributes, |
|
| 537 | 537 | } |
|
| 538 | 538 | ||
| 539 | 539 | /// Use declaration for importing modules. |
|
| 540 | - | export record Use { |
|
| 540 | + | export record Use: Copy { |
|
| 541 | 541 | /// Access node identifying the imported module. |
|
| 542 | 542 | path: *Node, |
|
| 543 | 543 | /// Whether this is a wildcard import (e.g. `use ast::*`). |
|
| 544 | 544 | wildcard: bool, |
|
| 545 | 545 | /// Optional attribute list applied to the use declaration. |
|
| 546 | 546 | attrs: ?Attributes, |
|
| 547 | 547 | } |
|
| 548 | 548 | ||
| 549 | 549 | /// Access expression used for field, scope, and index lookups. |
|
| 550 | - | export record Access { |
|
| 550 | + | export record Access: Copy { |
|
| 551 | 551 | /// Expression providing the container or namespace. |
|
| 552 | 552 | parent: *Node, |
|
| 553 | 553 | /// Expression identifying the member, scope element, or index. |
|
| 554 | 554 | child: *Node, |
|
| 555 | 555 | } |
|
| 556 | 556 | ||
| 557 | 557 | /// `as` cast expression metadata. |
|
| 558 | - | export record As { |
|
| 558 | + | export record As: Copy { |
|
| 559 | 559 | /// Expression being coerced. |
|
| 560 | 560 | value: *Node, |
|
| 561 | 561 | /// Target type annotation. |
|
| 562 | 562 | type: *Node, |
|
| 563 | 563 | } |
|
| 564 | 564 | ||
| 565 | 565 | /// Range expression metadata. |
|
| 566 | - | export record Range { |
|
| 566 | + | export record Range: Copy { |
|
| 567 | 567 | /// Optional inclusive start expression. |
|
| 568 | 568 | start: ?*Node, |
|
| 569 | 569 | /// Optional exclusive end expression. |
|
| 570 | 570 | end: ?*Node, |
|
| 571 | 571 | } |
|
| 572 | 572 | ||
| 573 | 573 | /// Binary operation expression, eg. `x * y`. |
|
| 574 | - | export record BinOp { |
|
| 574 | + | export record BinOp: Copy { |
|
| 575 | 575 | /// Operator applied to the operands. |
|
| 576 | 576 | op: BinaryOp, |
|
| 577 | 577 | /// Left-hand operand. |
|
| 578 | 578 | left: *Node, |
|
| 579 | 579 | /// Right-hand operand. |
|
| 580 | 580 | right: *Node, |
|
| 581 | 581 | } |
|
| 582 | 582 | ||
| 583 | 583 | /// Unary operation expression, eg. `-x`. |
|
| 584 | - | export record UnOp { |
|
| 584 | + | export record UnOp: Copy { |
|
| 585 | 585 | /// Operator applied to the operand. |
|
| 586 | 586 | op: UnaryOp, |
|
| 587 | 587 | /// Operand expression. |
|
| 588 | 588 | value: *Node, |
|
| 589 | 589 | } |
|
| 590 | 590 | ||
| 591 | 591 | /// Tagged union describing every possible AST node payload. |
|
| 592 | - | export union NodeValue { |
|
| 592 | + | export union NodeValue: Copy { |
|
| 593 | 593 | /// Placeholder `_` expression. |
|
| 594 | 594 | Placeholder, |
|
| 595 | 595 | /// Nil literal (`nil`). |
|
| 596 | 596 | Nil, |
|
| 597 | 597 | /// Undefined literal (`undefined`). |
| 793 | 793 | attrs: ?Attributes, |
|
| 794 | 794 | }, |
|
| 795 | 795 | } |
|
| 796 | 796 | ||
| 797 | 797 | /// Full AST node with shared metadata and variant-specific payload. |
|
| 798 | - | export record Node { |
|
| 798 | + | export record Node: Copy { |
|
| 799 | 799 | /// Unique identifier for this node. |
|
| 800 | 800 | id: u32, |
|
| 801 | 801 | /// Source span describing where the node originated. |
|
| 802 | 802 | span: Span, |
|
| 803 | 803 | /// Variant-specific payload for the node. |
| 835 | 835 | export fn synthNode(arena: *mut NodeArena, value: NodeValue) -> *mut Node { |
|
| 836 | 836 | return allocNode(arena, Span { offset: 0, length: 0 }, value); |
|
| 837 | 837 | } |
|
| 838 | 838 | ||
| 839 | 839 | /// Synthetic module with a single function in it. |
|
| 840 | - | record SynthFnMod { |
|
| 840 | + | record SynthFnMod: Copy { |
|
| 841 | 841 | /// The module block. |
|
| 842 | 842 | modBody: *Node, |
|
| 843 | 843 | /// The function block. |
|
| 844 | 844 | fnBody: *Node |
|
| 845 | 845 | } |
lib/std/lang/gen.rad
+1 -1
| 13 | 13 | /// Physical register. |
|
| 14 | 14 | /// |
|
| 15 | 15 | /// Lightweight wrapper around a register number. Used both in the |
|
| 16 | 16 | /// target-independent register allocator and in architecture-specific |
|
| 17 | 17 | /// backends. |
|
| 18 | - | export record Reg(u8); |
|
| 18 | + | export record Reg: Copy(u8); |
lib/std/lang/gen/bitset.rad
+2 -2
| 18 | 18 | export fn wordsFor(n: u32) -> u32 { |
|
| 19 | 19 | return (n + 31) / 32; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// A fixed-size bitset backed by an array of 32-bit words. |
|
| 23 | - | export record Bitset { |
|
| 23 | + | export record Bitset: Copy { |
|
| 24 | 24 | /// Backing storage for bits, organized as 32-bit words. |
|
| 25 | 25 | bits: *mut [u32], |
|
| 26 | 26 | /// Number of bits this bitset can hold. |
|
| 27 | 27 | len: u32, |
|
| 28 | 28 | } |
| 172 | 172 | set bs.bits[i] = 0; |
|
| 173 | 173 | } |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Iterator state for iterating set bits. |
|
| 177 | - | export record BitIter { |
|
| 177 | + | export record BitIter: Copy { |
|
| 178 | 178 | /// Bitset being iterated. |
|
| 179 | 179 | bs: *Bitset, |
|
| 180 | 180 | /// Current word index. |
|
| 181 | 181 | wordIdx: u32, |
|
| 182 | 182 | /// Remaining bits in the current word (visited bits cleared). |
lib/std/lang/gen/data.rad
+2 -2
| 14 | 14 | /// Size of the data symbol hash table. Must be a power of two |
|
| 15 | 15 | /// and at least twice the size of [`MAX_DATA_SYMS`]. |
|
| 16 | 16 | export constant DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2; |
|
| 17 | 17 | ||
| 18 | 18 | /// Data symbol entry mapping name to address. |
|
| 19 | - | export record DataSym { |
|
| 19 | + | export record DataSym: Copy { |
|
| 20 | 20 | /// Symbol name. |
|
| 21 | 21 | name: *[u8], |
|
| 22 | 22 | /// Absolute address, including data base address. |
|
| 23 | 23 | addr: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Hash-indexed data symbol map. |
|
| 27 | - | export record DataSymMap { |
|
| 27 | + | export record DataSymMap: Copy { |
|
| 28 | 28 | /// Underlying hash table. |
|
| 29 | 29 | dict: dict::Dict, |
|
| 30 | 30 | /// Fallback linear array for edge cases. |
|
| 31 | 31 | syms: *[DataSym], |
|
| 32 | 32 | } |
lib/std/lang/gen/labels.rad
+1 -1
| 10 | 10 | export constant MAX_FUNCS: u32 = 8192; |
|
| 11 | 11 | /// Size of the function hash table. Must be a power of two. |
|
| 12 | 12 | export constant FUNC_TABLE_SIZE: u32 = MAX_FUNCS * 2; |
|
| 13 | 13 | ||
| 14 | 14 | /// Label tracking for code emission. |
|
| 15 | - | export record Labels { |
|
| 15 | + | export record Labels: Copy { |
|
| 16 | 16 | /// Block offsets indexed by block index. |
|
| 17 | 17 | /// Per-function, reset each function. |
|
| 18 | 18 | blockOffsets: *mut [i32], |
|
| 19 | 19 | /// Number of blocks recorded in current function. |
|
| 20 | 20 | blockCount: u32, |
lib/std/lang/gen/regalloc.rad
+2 -2
| 20 | 20 | ||
| 21 | 21 | use std::lang::il; |
|
| 22 | 22 | use std::lang::alloc; |
|
| 23 | 23 | ||
| 24 | 24 | /// Target configuration for register allocation. |
|
| 25 | - | export record TargetConfig { |
|
| 25 | + | export record TargetConfig: Copy { |
|
| 26 | 26 | /// List of allocatable physical registers. |
|
| 27 | 27 | /// Order determines allocation preference. |
|
| 28 | 28 | allocatable: *[super::Reg], |
|
| 29 | 29 | /// Function argument registers. |
|
| 30 | 30 | argRegs: *[super::Reg], |
| 33 | 33 | /// Size of a spill slot in bytes. |
|
| 34 | 34 | slotSize: u32, |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Complete register allocation result. |
|
| 38 | - | export record AllocResult { |
|
| 38 | + | export record AllocResult: Copy { |
|
| 39 | 39 | /// SSA register to physical register mapping. |
|
| 40 | 40 | assignments: *[?super::Reg], |
|
| 41 | 41 | /// Spill slot information. |
|
| 42 | 42 | spill: spill::SpillInfo, |
|
| 43 | 43 | /// Bitmask of used callee-saved registers. |
lib/std/lang/gen/regalloc/assign.rad
+3 -3
| 16 | 16 | /// Maximum number of active register mappings. |
|
| 17 | 17 | constant MAX_ACTIVE: u32 = 64; |
|
| 18 | 18 | ||
| 19 | 19 | /// Register mapping at a program point. |
|
| 20 | 20 | /// Maps SSA registers to physical registers. |
|
| 21 | - | export record RegMap { |
|
| 21 | + | export record RegMap: Copy { |
|
| 22 | 22 | /// SSA (virtual) registers that have mappings. |
|
| 23 | 23 | virtRegs: *mut [u32], |
|
| 24 | 24 | /// Physical register for each virtual register. |
|
| 25 | 25 | physRegs: *mut [gen::Reg], |
|
| 26 | 26 | /// Number of active mappings. |
|
| 27 | 27 | n: u32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Register assignment result, per function. |
|
| 31 | - | export record AssignInfo { |
|
| 31 | + | export record AssignInfo: Copy { |
|
| 32 | 32 | /// SSA register -> physical register mapping. |
|
| 33 | 33 | assignments: *mut [?gen::Reg], |
|
| 34 | 34 | /// Bitmask of used callee-saved registers. |
|
| 35 | 35 | usedCalleeSaved: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Per-instruction context for freeing and allocating register uses. |
|
| 39 | - | record InstrCtx { |
|
| 39 | + | record InstrCtx: Copy { |
|
| 40 | 40 | current: *mut RegMap, |
|
| 41 | 41 | usedRegs: *mut bitset::Bitset, |
|
| 42 | 42 | func: *il::Fn, |
|
| 43 | 43 | live: *liveness::LiveInfo, |
|
| 44 | 44 | blockIdx: u32, |
lib/std/lang/gen/regalloc/liveness.rad
+3 -3
| 30 | 30 | ||
| 31 | 31 | /// Maximum number of SSA registers supported. |
|
| 32 | 32 | export constant MAX_SSA_REGS: u32 = 8192; |
|
| 33 | 33 | ||
| 34 | 34 | /// Liveness information for a function. |
|
| 35 | - | export record LiveInfo { |
|
| 35 | + | export record LiveInfo: Copy { |
|
| 36 | 36 | /// Per-block live-in sets (indexed by block index). |
|
| 37 | 37 | liveIn: *mut [bitset::Bitset], |
|
| 38 | 38 | /// Per-block live-out sets (indexed by block index). |
|
| 39 | 39 | liveOut: *mut [bitset::Bitset], |
|
| 40 | 40 | /// Per-block defs sets (registers defined in block). |
| 46 | 46 | /// Maximum register number used. |
|
| 47 | 47 | maxReg: u32, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Context for collecting defs and uses during block analysis. |
|
| 51 | - | record DefsUses { |
|
| 51 | + | record DefsUses: Copy { |
|
| 52 | 52 | defs: *bitset::Bitset, |
|
| 53 | 53 | uses: *mut bitset::Bitset, |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Context for searching for a specific register in an instruction. |
|
| 57 | - | record FindCtx { |
|
| 57 | + | record FindCtx: Copy { |
|
| 58 | 58 | target: u32, |
|
| 59 | 59 | found: bool, |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | 62 | /// Compute liveness information for a function. |
lib/std/lang/gen/regalloc/spill.rad
+5 -5
| 39 | 39 | constant MAX_CANDIDATES: u32 = 256; |
|
| 40 | 40 | /// Maximum loop depth for cost weighting (2^10 = 1024). |
|
| 41 | 41 | constant MAX_LOOP_WEIGHT: u32 = 10; |
|
| 42 | 42 | ||
| 43 | 43 | /// Spill cost for a single SSA register. |
|
| 44 | - | record SpillCost { |
|
| 44 | + | record SpillCost: Copy { |
|
| 45 | 45 | /// Number of definitions (weighted by loop depth). |
|
| 46 | 46 | defs: u32, |
|
| 47 | 47 | /// Number of uses (weighted by loop depth). |
|
| 48 | 48 | uses: u32, |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Spill decision for a function. |
|
| 52 | - | export record SpillInfo { |
|
| 52 | + | export record SpillInfo: Copy { |
|
| 53 | 53 | /// SSA register mapped to stack slot offset. `-1` means not spilled. |
|
| 54 | 54 | slots: *mut [i32], |
|
| 55 | 55 | /// Total spill frame size needed in bytes. |
|
| 56 | 56 | frameSize: i32, |
|
| 57 | 57 | /// Values that must be allocated in callee-saved registers. |
| 59 | 59 | /// Maximum SSA register number. |
|
| 60 | 60 | maxReg: u32, |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Candidate buffer for spill decisions. |
|
| 64 | - | record Candidates { |
|
| 64 | + | record Candidates: Copy { |
|
| 65 | 65 | entries: [CostEntry; 256], |
|
| 66 | 66 | n: u32, |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Entry for cost sorting. |
|
| 70 | - | record CostEntry { |
|
| 70 | + | record CostEntry: Copy { |
|
| 71 | 71 | reg: u32, |
|
| 72 | 72 | cost: u32, |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Context for counting register uses. |
|
| 76 | - | record CountCtx { |
|
| 76 | + | record CountCtx: Copy { |
|
| 77 | 77 | costs: *mut [SpillCost], |
|
| 78 | 78 | weight: u32, |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Analyze a function and determine which values need spill slots. |
lib/std/lang/gen/types.rad
+2 -2
| 1 | 1 | //! Target-independent code generation types. |
|
| 2 | 2 | //! |
|
| 3 | 3 | //! Defines common records used by all code generation backends. |
|
| 4 | 4 | ||
| 5 | 5 | /// Function address entry for printing. |
|
| 6 | - | export record FuncAddr { |
|
| 6 | + | export record FuncAddr: Copy { |
|
| 7 | 7 | /// Function name. |
|
| 8 | 8 | name: *[u8], |
|
| 9 | 9 | /// Instruction index where this function starts. |
|
| 10 | 10 | index: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Debug entry mapping an instruction to a source location. |
|
| 14 | - | export record DebugEntry { |
|
| 14 | + | export record DebugEntry: Copy { |
|
| 15 | 15 | /// Byte offset of the instruction from the start of the program. |
|
| 16 | 16 | pc: u32, |
|
| 17 | 17 | /// Module identifier. |
|
| 18 | 18 | moduleId: u16, |
|
| 19 | 19 | /// Byte offset into the module's source file. |
lib/std/lang/il.rad
+16 -16
| 65 | 65 | ||
| 66 | 66 | /// Source location for debug info. |
|
| 67 | 67 | /// |
|
| 68 | 68 | /// Associates an IL instruction with its originating source module and |
|
| 69 | 69 | /// byte offset. |
|
| 70 | - | export record SrcLoc { |
|
| 70 | + | export record SrcLoc: Copy { |
|
| 71 | 71 | /// Module identifier. |
|
| 72 | 72 | moduleId: u16, |
|
| 73 | 73 | /// Byte offset into the module's source file. |
|
| 74 | 74 | offset: u32, |
|
| 75 | 75 | } |
| 102 | 102 | /////////// |
|
| 103 | 103 | // Types // |
|
| 104 | 104 | /////////// |
|
| 105 | 105 | ||
| 106 | 106 | /// IL type representation. Integer signedness is encoded in operations, not types. |
|
| 107 | - | export union Type { |
|
| 107 | + | export union Type: Copy { |
|
| 108 | 108 | /// 8-bit value. |
|
| 109 | 109 | W8, |
|
| 110 | 110 | /// 16-bit value. |
|
| 111 | 111 | W16, |
|
| 112 | 112 | /// 32-bit value. |
| 124 | 124 | case Type::W64 => return 8, |
|
| 125 | 125 | } |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// SSA register reference. |
|
| 129 | - | export record Reg { n: u32 } |
|
| 129 | + | export record Reg: Copy { n: u32 } |
|
| 130 | 130 | ||
| 131 | 131 | /// Instruction value. |
|
| 132 | - | export union Val { |
|
| 132 | + | export union Val: Copy { |
|
| 133 | 133 | /// Register reference. |
|
| 134 | 134 | Reg(Reg), |
|
| 135 | 135 | /// Immediate integer value. |
|
| 136 | 136 | Imm(i64), |
|
| 137 | 137 | /// Data symbol address (globals, constants, string literals). |
| 143 | 143 | /// eg. in void returns. |
|
| 144 | 144 | Undef, |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Comparison operation for compare-and-branch. |
|
| 148 | - | export union CmpOp { Eq, Ne, Slt, Ult } |
|
| 148 | + | export union CmpOp: Copy { Eq, Ne, Slt, Ult } |
|
| 149 | 149 | ||
| 150 | 150 | /// Binary ALU operation kind. |
|
| 151 | - | export union BinOp { |
|
| 151 | + | export union BinOp: Copy { |
|
| 152 | 152 | // Arithmetic. |
|
| 153 | 153 | Add, Sub, Mul, Sdiv, Udiv, Srem, Urem, |
|
| 154 | 154 | // Comparison. |
|
| 155 | 155 | Eq, Ne, Slt, Sge, Ult, Uge, |
|
| 156 | 156 | // Bitwise. |
|
| 157 | 157 | And, Or, Xor, Shl, Sshr, Ushr, |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Unary ALU operation kind. |
|
| 161 | - | export union UnOp { |
|
| 161 | + | export union UnOp: Copy { |
|
| 162 | 162 | Neg, Not, |
|
| 163 | 163 | } |
|
| 164 | 164 | ||
| 165 | 165 | ////////////////// |
|
| 166 | 166 | // Instructions // |
|
| 167 | 167 | ////////////////// |
|
| 168 | 168 | ||
| 169 | 169 | /// Block parameter. |
|
| 170 | - | export record Param { |
|
| 170 | + | export record Param: Copy { |
|
| 171 | 171 | /// SSA register. |
|
| 172 | 172 | value: Reg, |
|
| 173 | 173 | /// Parameter type. |
|
| 174 | 174 | type: Type, |
|
| 175 | 175 | } |
|
| 176 | 176 | ||
| 177 | 177 | /// Switch case mapping a constant value to a branch target. |
|
| 178 | - | export record SwitchCase { |
|
| 178 | + | export record SwitchCase: Copy { |
|
| 179 | 179 | /// The constant value to match against. |
|
| 180 | 180 | value: i64, |
|
| 181 | 181 | /// The target block index. |
|
| 182 | 182 | target: u32, |
|
| 183 | 183 | /// Arguments to pass to the target block. |
|
| 184 | 184 | args: *mut [Val], |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// IL instruction. |
|
| 188 | 188 | /// SSA registers are represented as `Reg`, values as `Val`. |
|
| 189 | - | export union Instr { |
|
| 189 | + | export union Instr: Copy { |
|
| 190 | 190 | /////////////////////// |
|
| 191 | 191 | // Memory operations // |
|
| 192 | 192 | /////////////////////// |
|
| 193 | 193 | ||
| 194 | 194 | /// Allocate space on the stack: `reserve %dst <size> <alignment>;` |
| 307 | 307 | /// |
|
| 308 | 308 | /// Basic blocks are instruction sequences with a single entry point and no branch |
|
| 309 | 309 | /// instruction, except possibly at the end of the sequence, where a terminator |
|
| 310 | 310 | /// may be found, ie. an instruction that terminates the sequence by jumping |
|
| 311 | 311 | /// to another sequence. |
|
| 312 | - | export record Block { |
|
| 312 | + | export record Block: Copy { |
|
| 313 | 313 | /// Block label. |
|
| 314 | 314 | label: *[u8], |
|
| 315 | 315 | /// Block parameters. |
|
| 316 | 316 | params: *[Param], |
|
| 317 | 317 | /// Instructions in the block. The last instruction must be a terminator. |
| 325 | 325 | /// Used for spill cost weighting in register allocation. |
|
| 326 | 326 | loopDepth: u32, |
|
| 327 | 327 | } |
|
| 328 | 328 | ||
| 329 | 329 | /// An IL function. |
|
| 330 | - | export record Fn { |
|
| 330 | + | export record Fn: Copy { |
|
| 331 | 331 | /// Qualified function name (e.g. `$mod$path$func`). |
|
| 332 | 332 | name: *[u8], |
|
| 333 | 333 | /// Function parameters. |
|
| 334 | 334 | params: *[Param], |
|
| 335 | 335 | /// Return type. |
| 345 | 345 | ///////////// |
|
| 346 | 346 | // Program // |
|
| 347 | 347 | ///////////// |
|
| 348 | 348 | ||
| 349 | 349 | /// Data initializer item. |
|
| 350 | - | export union DataItem { |
|
| 350 | + | export union DataItem: Copy { |
|
| 351 | 351 | /// Typed value: `w32 42;` or `w8 255;` |
|
| 352 | 352 | Val { typ: Type, val: i64 }, |
|
| 353 | 353 | /// Symbol reference: `$symbol` |
|
| 354 | 354 | Sym(*[u8]), |
|
| 355 | 355 | /// Function reference: `$fnName` |
| 359 | 359 | /// Undefined value. Used for padding and `void` returns. |
|
| 360 | 360 | Undef, |
|
| 361 | 361 | } |
|
| 362 | 362 | ||
| 363 | 363 | /// Data initializer value with optional repeat count. |
|
| 364 | - | export record DataValue { |
|
| 364 | + | export record DataValue: Copy { |
|
| 365 | 365 | /// The item contained in the value. |
|
| 366 | 366 | item: DataItem, |
|
| 367 | 367 | /// The number of times the item should be repeated. |
|
| 368 | 368 | count: u32, |
|
| 369 | 369 | } |
|
| 370 | 370 | ||
| 371 | 371 | /// Global data definition. |
|
| 372 | - | export record Data { |
|
| 372 | + | export record Data: Copy { |
|
| 373 | 373 | /// Data name. |
|
| 374 | 374 | name: *[u8], |
|
| 375 | 375 | /// Size in bytes. |
|
| 376 | 376 | size: u32, |
|
| 377 | 377 | /// Alignment requirement. |
| 386 | 386 | /// Initializer values. |
|
| 387 | 387 | values: *[DataValue], |
|
| 388 | 388 | } |
|
| 389 | 389 | ||
| 390 | 390 | /// An IL program (compilation unit). |
|
| 391 | - | export record Program { |
|
| 391 | + | export record Program: Copy { |
|
| 392 | 392 | /// Global data. |
|
| 393 | 393 | data: *[Data], |
|
| 394 | 394 | /// Functions. |
|
| 395 | 395 | fns: *[*Fn], |
|
| 396 | 396 | } |
lib/std/lang/lower.rad
+25 -25
| 107 | 107 | // Error Handling // |
|
| 108 | 108 | //////////////////// |
|
| 109 | 109 | ||
| 110 | 110 | /// Lowering errors are typically unrecoverable since they indicate bugs in |
|
| 111 | 111 | /// the resolver or malformed AST that should have been caught earlier. |
|
| 112 | - | export union LowerError { |
|
| 112 | + | export union LowerError: Copy { |
|
| 113 | 113 | /// A node's symbol was not set before lowering. |
|
| 114 | 114 | MissingSymbol(*ast::Node), |
|
| 115 | 115 | /// A node's type was not set before lowering. |
|
| 116 | 116 | MissingType(*ast::Node), |
|
| 117 | 117 | /// A node's constant value was not set before lowering. |
| 247 | 247 | ////////////////////////// |
|
| 248 | 248 | // Core Data Structures // |
|
| 249 | 249 | ////////////////////////// |
|
| 250 | 250 | ||
| 251 | 251 | /// Options controlling the lowering pass. |
|
| 252 | - | export record LowerOptions { |
|
| 252 | + | export record LowerOptions: Copy { |
|
| 253 | 253 | /// Whether to emit source location info. |
|
| 254 | 254 | debug: bool, |
|
| 255 | 255 | /// Whether to lower `@test` functions. |
|
| 256 | 256 | buildTest: bool, |
|
| 257 | 257 | } |
|
| 258 | 258 | ||
| 259 | 259 | /// Role of a lowered function in the final program. |
|
| 260 | - | export union FnRole { |
|
| 260 | + | export union FnRole: Copy { |
|
| 261 | 261 | /// Regular function. |
|
| 262 | 262 | Normal, |
|
| 263 | 263 | /// Program entry function marked with `@default`. |
|
| 264 | 264 | Default, |
|
| 265 | 265 | } |
|
| 266 | 266 | ||
| 267 | 267 | /// Function sink used by lowerers that consume functions as they are produced. |
|
| 268 | - | export record FnSink { |
|
| 268 | + | export record FnSink: Copy { |
|
| 269 | 269 | /// Opaque context passed to the sink callback. |
|
| 270 | 270 | ctx: *mut opaque, |
|
| 271 | 271 | /// Callback invoked for each lowered function. |
|
| 272 | 272 | emitFn: fn(*mut opaque, *il::Fn, FnRole), |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Destination for functions produced by the lowerer. |
|
| 276 | - | export union FnOutput { |
|
| 276 | + | export union FnOutput: Copy { |
|
| 277 | 277 | /// Store lowered functions in the provided slice. |
|
| 278 | 278 | Accumulate(*mut [*il::Fn]), |
|
| 279 | 279 | /// Send lowered functions to an external consumer immediately. |
|
| 280 | 280 | Stream(FnSink), |
|
| 281 | 281 | } |
|
| 282 | 282 | ||
| 283 | 283 | /// Module-level lowering context. Shared across all function lowerings. |
|
| 284 | 284 | /// Holds global state like the data section (strings, constants) and provides |
|
| 285 | 285 | /// access to the resolver for type queries. |
|
| 286 | - | export record Lowerer { |
|
| 286 | + | export record Lowerer: Copy { |
|
| 287 | 287 | /// Arena for persistent lowering state, including data and symbol names. |
|
| 288 | 288 | arena: *mut alloc::Arena, |
|
| 289 | 289 | /// Arena for allocations owned by the function currently being lowered. |
|
| 290 | 290 | fnArena: *mut alloc::Arena, |
|
| 291 | 291 | /// Allocator backed by the arena. |
| 313 | 313 | /// Lowering options. |
|
| 314 | 314 | options: LowerOptions, |
|
| 315 | 315 | } |
|
| 316 | 316 | ||
| 317 | 317 | /// Entry mapping a function symbol to its qualified name. |
|
| 318 | - | record FnSymEntry { |
|
| 318 | + | record FnSymEntry: Copy { |
|
| 319 | 319 | sym: *resolver::Symbol, |
|
| 320 | 320 | qualName: *[u8], |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | 323 | /// Entry in the global error tag table. |
|
| 324 | - | record ErrTagEntry { |
|
| 324 | + | record ErrTagEntry: Copy { |
|
| 325 | 325 | /// The type of this error, identified by its interned pointer. |
|
| 326 | 326 | ty: resolver::Type, |
|
| 327 | 327 | /// The globally unique tag assigned to this error type (non-zero). |
|
| 328 | 328 | tag: u32, |
|
| 329 | 329 | } |
| 377 | 377 | } |
|
| 378 | 378 | return FnRole::Normal; |
|
| 379 | 379 | } |
|
| 380 | 380 | ||
| 381 | 381 | /// Builder for accumulating data values during constant lowering. |
|
| 382 | - | record DataValueBuilder { |
|
| 382 | + | record DataValueBuilder: Copy { |
|
| 383 | 383 | allocator: alloc::Allocator, |
|
| 384 | 384 | values: *mut [il::DataValue], |
|
| 385 | 385 | /// Whether all pushed values can be represented by zero-filled memory. |
|
| 386 | 386 | zeroInit: bool, |
|
| 387 | 387 | } |
|
| 388 | 388 | ||
| 389 | 389 | /// Result of lowering constant data. |
|
| 390 | - | record ConstDataResult { |
|
| 390 | + | record ConstDataResult: Copy { |
|
| 391 | 391 | values: *[il::DataValue], |
|
| 392 | 392 | zeroInit: bool, |
|
| 393 | 393 | } |
|
| 394 | 394 | ||
| 395 | 395 | /// Create a new builder. |
| 442 | 442 | // a mapping from [`Var`] to current SSA value. When control flow merges, |
|
| 443 | 443 | // block parameters are inserted to merge values from different control flow paths. |
|
| 444 | 444 | ||
| 445 | 445 | /// A variable handle. Represents a source-level variable during lowering. |
|
| 446 | 446 | /// The same [`Var`] can have different SSA values in different blocks. |
|
| 447 | - | export record Var(u32); |
|
| 447 | + | export record Var: Copy(u32); |
|
| 448 | 448 | ||
| 449 | 449 | /// Metadata for a source-level variable, stored once per function. |
|
| 450 | 450 | /// |
|
| 451 | 451 | /// Each variable declaration in the source creates one [`VarData`] entry in the |
|
| 452 | 452 | /// function's `variables` array, indexed by `id`. This contains static |
| 454 | 454 | /// |
|
| 455 | 455 | /// Per-block SSA values are tracked separately in [`BlockData::vars`] as [`?il::Val`], |
|
| 456 | 456 | /// where `nil` means "not yet assigned in this block". Together they implement |
|
| 457 | 457 | /// SSA construction. |
|
| 458 | 458 | /// |
|
| 459 | - | record VarData { |
|
| 459 | + | record VarData: Copy { |
|
| 460 | 460 | /// Variable name, used by [`lookupVarByName`] to resolve identifiers. |
|
| 461 | 461 | /// Nil for anonymous variables (e.g., internal loop counters). |
|
| 462 | 462 | name: ?*[u8], |
|
| 463 | 463 | /// IL type of this variable. Set at declaration time and used when |
|
| 464 | 464 | /// generating loads, stores, and type-checking assignments. |
| 472 | 472 | addressTaken: bool, |
|
| 473 | 473 | } |
|
| 474 | 474 | ||
| 475 | 475 | /// Links a function parameter to its corresponding variable for the entry block. |
|
| 476 | 476 | /// After creating the entry block, we iterate through these to define initial values. |
|
| 477 | - | record FnParamBinding { |
|
| 477 | + | record FnParamBinding: Copy { |
|
| 478 | 478 | /// The variable that receives this parameter's value. |
|
| 479 | 479 | var: Var, |
|
| 480 | 480 | /// SSA register containing the parameter value from the caller. |
|
| 481 | 481 | reg: il::Reg, |
|
| 482 | 482 | } |
| 489 | 489 | // instructions until terminated by a jump, branch, return, or unreachable. |
|
| 490 | 490 | // Blocks can be created before they're filled (forward references for jumps). |
|
| 491 | 491 | ||
| 492 | 492 | /// A handle to a basic block within the current function. |
|
| 493 | 493 | /// Block handles are stable, they don't change as more blocks are added. |
|
| 494 | - | export record BlockId(u32); |
|
| 494 | + | export record BlockId: Copy(u32); |
|
| 495 | 495 | ||
| 496 | 496 | /// Internal block state during construction. |
|
| 497 | 497 | /// |
|
| 498 | 498 | /// The key invariants: |
|
| 499 | 499 | /// |
|
| 500 | 500 | /// - A block is "open" if it has no terminator; instructions can be added. |
|
| 501 | 501 | /// - A block is "sealed" when all predecessor edges are known. |
|
| 502 | 502 | /// - Sealing is required before SSA construction can insert block parameters. |
|
| 503 | 503 | /// |
|
| 504 | 504 | /// This differs from the final [`il::Block`] which is immutable and fully formed. |
|
| 505 | - | record BlockData { |
|
| 505 | + | record BlockData: Copy { |
|
| 506 | 506 | /// Block label for debugging and IL printing. |
|
| 507 | 507 | label: *[u8], |
|
| 508 | 508 | /// Block parameters for merging values at control flow joins. These |
|
| 509 | 509 | /// receive values from predecessor edges when control flow merges. |
|
| 510 | 510 | params: *mut [il::Param], |
| 535 | 535 | /// |
|
| 536 | 536 | /// A block is "unsealed" while its predecessors are still being discovered. |
|
| 537 | 537 | /// During this time, variables used before being defined locally are tracked. |
|
| 538 | 538 | /// Once all predecessors are known, the block is sealed and those variables |
|
| 539 | 539 | /// are resolved via [`resolveBlockArgs`]. |
|
| 540 | - | union Sealed { |
|
| 540 | + | union Sealed: Copy { |
|
| 541 | 541 | /// Block is unsealed; predecessors may still be added. |
|
| 542 | 542 | No { incompleteVars: *mut [u32] }, |
|
| 543 | 543 | /// Block is sealed; all predecessors are known. |
|
| 544 | 544 | Yes, |
|
| 545 | 545 | } |
| 548 | 548 | // Loop and Control Flow Context // |
|
| 549 | 549 | /////////////////////////////////// |
|
| 550 | 550 | ||
| 551 | 551 | /// Context for break/continue statements within a loop. |
|
| 552 | 552 | /// Each nested loop pushes a new context onto the loop stack. |
|
| 553 | - | export record LoopCtx { |
|
| 553 | + | export record LoopCtx: Copy { |
|
| 554 | 554 | /// Where `break` should transfer control (the loop's exit block). |
|
| 555 | 555 | breakTarget: BlockId, |
|
| 556 | 556 | /// Where `continue` should transfer control. |
|
| 557 | 557 | continueTarget: ?BlockId, |
|
| 558 | 558 | } |
|
| 559 | 559 | ||
| 560 | 560 | /// Logical operator. |
|
| 561 | - | union LogicalOp { And, Or } |
|
| 561 | + | union LogicalOp: Copy { And, Or } |
|
| 562 | 562 | ||
| 563 | 563 | /// Iterator state for for-loop lowering. |
|
| 564 | - | union ForIter { |
|
| 564 | + | union ForIter: Copy { |
|
| 565 | 565 | /// Range iterator: `for i in 0..n`. |
|
| 566 | 566 | Range { |
|
| 567 | 567 | valVar: Var, |
|
| 568 | 568 | indexVar: ?Var, |
|
| 569 | 569 | endVal: il::Val, |
| 592 | 592 | // - Optional aggregates: tag checked then payload extracted. |
|
| 593 | 593 | // - Unions: tag compared against variant indices. |
|
| 594 | 594 | ||
| 595 | 595 | /// Cached information about a match subject. Computed once and reused across |
|
| 596 | 596 | /// all arms to avoid redundant lowering and type queries. |
|
| 597 | - | record MatchSubject { |
|
| 597 | + | record MatchSubject: Copy { |
|
| 598 | 598 | /// The lowered subject value. |
|
| 599 | 599 | val: il::Val, |
|
| 600 | 600 | /// Source-level type from the resolver. |
|
| 601 | 601 | type: resolver::Type, |
|
| 602 | 602 | /// IL-level type for code generation. |
| 609 | 609 | /// How bindings are created: by value, or by reference. |
|
| 610 | 610 | by: resolver::MatchBy, |
|
| 611 | 611 | } |
|
| 612 | 612 | ||
| 613 | 613 | /// Classifies a match subject by how it should be compared and destructured. |
|
| 614 | - | union MatchSubjectKind { |
|
| 614 | + | union MatchSubjectKind: Copy { |
|
| 615 | 615 | /// Regular value: direct equality comparison. |
|
| 616 | 616 | Regular, |
|
| 617 | 617 | /// Optional with null pointer optimization: `?*T`, `?*[T]`. |
|
| 618 | 618 | OptionalPtr, |
|
| 619 | 619 | /// Optional aggregate `?T`: tagged union with payload. |
| 639 | 639 | ////////////////////////// |
|
| 640 | 640 | // Field Access Support // |
|
| 641 | 641 | ////////////////////////// |
|
| 642 | 642 | ||
| 643 | 643 | /// Result of resolving a place expression to a memory location. |
|
| 644 | - | record FieldRef { |
|
| 644 | + | record FieldRef: Copy { |
|
| 645 | 645 | /// Base pointer register (points to the container). |
|
| 646 | 646 | base: il::Reg, |
|
| 647 | 647 | /// Byte offset of the value within the container. |
|
| 648 | 648 | offset: i32, |
|
| 649 | 649 | /// Type of the value held at that location. |
| 651 | 651 | } |
|
| 652 | 652 | ||
| 653 | 653 | /// Result of computing an element pointer for array/slice subscript operations. |
|
| 654 | 654 | /// Used by [`lowerElemPtr`] to return both the element address register and |
|
| 655 | 655 | /// the element type for subsequent load or address-of operations. |
|
| 656 | - | record ElemPtrResult { |
|
| 656 | + | record ElemPtrResult: Copy { |
|
| 657 | 657 | /// Register holding the computed element address. |
|
| 658 | 658 | elemReg: il::Reg, |
|
| 659 | 659 | /// Source-level type of the element. |
|
| 660 | 660 | elemType: resolver::Type, |
|
| 661 | 661 | } |
|
| 662 | 662 | ||
| 663 | 663 | /// Result of resolving a slice range to a data pointer and element count. |
|
| 664 | - | record SliceRangeResult { |
|
| 664 | + | record SliceRangeResult: Copy { |
|
| 665 | 665 | dataReg: il::Reg, |
|
| 666 | 666 | count: il::Val, |
|
| 667 | 667 | } |
|
| 668 | 668 | ||
| 669 | 669 | ///////////////////////////// |
|
| 670 | 670 | // Function Lowering State // |
|
| 671 | 671 | ///////////////////////////// |
|
| 672 | 672 | ||
| 673 | 673 | /// Per-function lowering state. Created fresh for each function and contains |
|
| 674 | 674 | /// all the mutable state needed during function body lowering. |
|
| 675 | - | record FnLowerer { |
|
| 675 | + | record FnLowerer: Copy { |
|
| 676 | 676 | /// Reference to the module-level lowerer. |
|
| 677 | 677 | low: *mut Lowerer, |
|
| 678 | 678 | /// Allocator for IL allocations. |
|
| 679 | 679 | allocator: alloc::Allocator, |
|
| 680 | 680 | /// Type signature of the function being lowered. |
lib/std/lang/module.rad
+4 -4
| 23 | 23 | constant PATH_SEP: u8 = '/'; |
|
| 24 | 24 | /// Source file extension handled by the loader. |
|
| 25 | 25 | constant SOURCE_EXT: *[u8] = ".rad"; |
|
| 26 | 26 | ||
| 27 | 27 | /// Lifecycle state for modules in the dependency graph. |
|
| 28 | - | export union ModuleState { |
|
| 28 | + | export union ModuleState: Copy { |
|
| 29 | 29 | /// Slot unused or yet to be initialized. |
|
| 30 | 30 | Vacant, |
|
| 31 | 31 | /// Module registered with a known path but not parsed yet. |
|
| 32 | 32 | Registered, |
|
| 33 | 33 | /// Module is currently being parsed (used for cycle detection). |
| 39 | 39 | /// Module finished semantic analysis and is ready for codegen. |
|
| 40 | 40 | Ready, |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Error categories that can be produced by the module graph. |
|
| 44 | - | export union ModuleError { |
|
| 44 | + | export union ModuleError: Copy { |
|
| 45 | 45 | /// Module not found. |
|
| 46 | 46 | NotFound(u16), |
|
| 47 | 47 | /// Adding the module would exceed the fixed storage. |
|
| 48 | 48 | CapacityExceeded, |
|
| 49 | 49 | /// Provided path is missing the required `.rad` extension or otherwise invalid. |
| 53 | 53 | /// Attempt to register a module before its parent. |
|
| 54 | 54 | MissingParent, |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Module metadata recorded inside the dependency graph. |
|
| 58 | - | export record ModuleEntry { |
|
| 58 | + | export record ModuleEntry: Copy { |
|
| 59 | 59 | /// Numeric identifier for the slot (index in the graph array). |
|
| 60 | 60 | id: u16, |
|
| 61 | 61 | /// Package identifier this module belongs to. |
|
| 62 | 62 | packageId: u16, |
|
| 63 | 63 | /// Parent module identifier, or `nil` for root modules. |
| 83 | 83 | /// Source text for this module (for error reporting). |
|
| 84 | 84 | source: ?*[u8], |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 88 | - | export record ModuleGraph { |
|
| 88 | + | export record ModuleGraph: Copy { |
|
| 89 | 89 | entries: *mut [ModuleEntry], |
|
| 90 | 90 | entriesLen: u32, |
|
| 91 | 91 | pool: *mut strings::Pool, |
|
| 92 | 92 | /// Arena used for all AST node allocations. |
|
| 93 | 93 | arena: ?*ast::NodeArena, |
lib/std/lang/package.rad
+1 -1
| 7 | 7 | ||
| 8 | 8 | /// Maximum number of packages processed in a single invocation. |
|
| 9 | 9 | export constant MAX_PACKAGES: u32 = 4; |
|
| 10 | 10 | ||
| 11 | 11 | /// A compilation unit. |
|
| 12 | - | export record Package { |
|
| 12 | + | export record Package: Copy { |
|
| 13 | 13 | /// Package identifier (index in the package array). |
|
| 14 | 14 | id: u16, |
|
| 15 | 15 | /// Package name. |
|
| 16 | 16 | name: *[u8], |
|
| 17 | 17 | /// Root module identifier for this package, or `nil` if not yet registered. |
lib/std/lang/parser.rad
+9 -9
| 25 | 25 | ||
| 26 | 26 | /// Maximum number of parser errors before aborting. |
|
| 27 | 27 | constant MAX_ERRORS: u32 = 8; |
|
| 28 | 28 | ||
| 29 | 29 | /// Parser error type. |
|
| 30 | - | export union ParseError { |
|
| 30 | + | export union ParseError: Copy { |
|
| 31 | 31 | /// Encountered a token that was not expected in the current context. |
|
| 32 | 32 | UnexpectedToken, |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Represents a parsed name-type-value triple. |
|
| 36 | 36 | /// |
|
| 37 | 37 | /// Used for record field declarations, variable declarations, |
|
| 38 | 38 | /// and record field initializations. |
|
| 39 | - | record NameTypeValue { |
|
| 39 | + | record NameTypeValue: Copy { |
|
| 40 | 40 | /// The identifier name. |
|
| 41 | 41 | name: *ast::Node, |
|
| 42 | 42 | /// The optional type annotation. |
|
| 43 | 43 | type: ?*ast::Node, |
|
| 44 | 44 | /// The optional initialization value. |
| 46 | 46 | /// The optional alignment specifier. |
|
| 47 | 47 | alignment: ?*ast::Node, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Behavioural differences when parsing record field lists. |
|
| 51 | - | union RecordFieldMode { |
|
| 51 | + | union RecordFieldMode: Copy { |
|
| 52 | 52 | /// Labeled fields, allows for default values. |
|
| 53 | 53 | Labeled, |
|
| 54 | 54 | /// Unlabeled fields. |
|
| 55 | 55 | Unlabeled, |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | 58 | /// Parser context differentiates between regular expressions and |
|
| 59 | 59 | /// conditional contexts where `{` begins a block. |
|
| 60 | - | union Context { |
|
| 60 | + | union Context: Copy { |
|
| 61 | 61 | /// Normal expression context where `{` may start a record literal |
|
| 62 | 62 | /// and `if` may start a conditional expression. |
|
| 63 | 63 | Normal, |
|
| 64 | 64 | /// Pattern context where `{` may start a record literal |
|
| 65 | 65 | /// but `if` is reserved for guards. |
| 68 | 68 | /// and `if` is reserved for guards. |
|
| 69 | 69 | Condition, |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// A single parser error with location information. |
|
| 73 | - | export record Error { |
|
| 73 | + | export record Error: Copy { |
|
| 74 | 74 | /// Human-readable error message. |
|
| 75 | 75 | message: *[u8], |
|
| 76 | 76 | /// The token where the error occurred. |
|
| 77 | 77 | token: scanner::Token, |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// List of parser errors encountered during parsing. |
|
| 81 | - | record ErrorList { |
|
| 81 | + | record ErrorList: Copy { |
|
| 82 | 82 | /// Fixed-size array of error records. |
|
| 83 | 83 | list: [Error; MAX_ERRORS], |
|
| 84 | 84 | /// Number of errors currently in the list. |
|
| 85 | 85 | count: u32, |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | 88 | /// Snapshot of parser state for speculative parsing. |
|
| 89 | - | record SavedState { |
|
| 89 | + | record SavedState: Copy { |
|
| 90 | 90 | parser: Parser, |
|
| 91 | 91 | arena: u32, |
|
| 92 | 92 | nextId: u32, |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | /// Operator metadata for precedence climbing. |
|
| 96 | - | record OpInfo { |
|
| 96 | + | record OpInfo: Copy { |
|
| 97 | 97 | op: ast::BinaryOp, |
|
| 98 | 98 | prec: i32, |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | 101 | /// Get operator info for a token kind using match-based dispatch. |
| 142 | 142 | return nil, |
|
| 143 | 143 | } |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | /// Parser state. |
|
| 147 | - | export record Parser { |
|
| 147 | + | export record Parser: Copy { |
|
| 148 | 148 | /// The scanner that provides tokens. |
|
| 149 | 149 | scanner: scanner::Scanner, |
|
| 150 | 150 | /// The current token being examined. |
|
| 151 | 151 | current: scanner::Token, |
|
| 152 | 152 | /// The most recently consumed token. |
lib/std/lang/resolver.rad
+231 -114
| 51 | 51 | constant MAX_LINEAR_BINDINGS: u32 = 32; |
|
| 52 | 52 | /// Maximum nesting depth tracked for loops. |
|
| 53 | 53 | constant MAX_LINEAR_LOOP_DEPTH: u32 = 16; |
|
| 54 | 54 | ||
| 55 | 55 | /// Trait definition stored in the resolver. |
|
| 56 | - | export record TraitType { |
|
| 56 | + | export record TraitType: Copy { |
|
| 57 | 57 | /// Trait name. |
|
| 58 | 58 | name: *[u8], |
|
| 59 | 59 | /// Method signatures, including from supertraits. |
|
| 60 | 60 | methods: *mut [TraitMethod], |
|
| 61 | 61 | /// Supertraits that must also be implemented. |
|
| 62 | 62 | supertraits: *mut [*TraitType], |
|
| 63 | 63 | } |
|
| 64 | 64 | ||
| 65 | 65 | /// A single method signature within a trait. |
|
| 66 | - | export record TraitMethod { |
|
| 66 | + | export record TraitMethod: Copy { |
|
| 67 | 67 | /// Method name. |
|
| 68 | 68 | name: *[u8], |
|
| 69 | 69 | /// Function type for the method, excluding the receiver. |
|
| 70 | 70 | fnType: *FnType, |
|
| 71 | 71 | /// Whether the receiver is mutable. |
| 75 | 75 | /// V-table slot index. |
|
| 76 | 76 | index: u32, |
|
| 77 | 77 | } |
|
| 78 | 78 | ||
| 79 | 79 | /// An entry in the trait instance registry. |
|
| 80 | - | export record InstanceEntry { |
|
| 80 | + | export record InstanceEntry: Copy { |
|
| 81 | 81 | /// Trait type descriptor. |
|
| 82 | 82 | traitType: *TraitType, |
|
| 83 | 83 | /// Concrete type that implements the trait. |
|
| 84 | 84 | concreteType: Type, |
|
| 85 | 85 | /// Name of the concrete type. |
| 89 | 89 | /// Method symbols for each trait method, in declaration order. |
|
| 90 | 90 | methods: *mut [*mut Symbol], |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// An entry in the method registry. |
|
| 94 | - | export record MethodEntry { |
|
| 94 | + | export record MethodEntry: Copy { |
|
| 95 | 95 | /// Concrete type that owns the method. |
|
| 96 | 96 | concreteType: Type, |
|
| 97 | 97 | /// Name of the concrete type. |
|
| 98 | 98 | concreteTypeName: *[u8], |
|
| 99 | 99 | /// Method name. |
| 140 | 140 | ||
| 141 | 141 | /// Size of a pointer in bytes. |
|
| 142 | 142 | export constant PTR_SIZE: u32 = 8; |
|
| 143 | 143 | ||
| 144 | 144 | /// Information about a record or tuple field. |
|
| 145 | - | export record RecordField { |
|
| 145 | + | export record RecordField: Copy { |
|
| 146 | 146 | /// Field name, `nil` for positional fields. |
|
| 147 | 147 | name: ?*[u8], |
|
| 148 | 148 | /// Field type. |
|
| 149 | 149 | fieldType: Type, |
|
| 150 | 150 | /// Byte offset from the start of the record. |
|
| 151 | 151 | offset: i32, |
|
| 152 | 152 | } |
|
| 153 | 153 | ||
| 154 | 154 | /// Information about a union variant. |
|
| 155 | - | record UnionVariant { |
|
| 155 | + | record UnionVariant: Copy { |
|
| 156 | 156 | name: *[u8], |
|
| 157 | 157 | valueType: Type, |
|
| 158 | 158 | symbol: *mut Symbol, |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Array type payload. |
|
| 162 | - | export record ArrayType { |
|
| 162 | + | export record ArrayType: Copy { |
|
| 163 | 163 | item: *Type, |
|
| 164 | 164 | length: u32, |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | 167 | /// Record nominal type. |
|
| 168 | - | export record RecordType { |
|
| 168 | + | export record RecordType: Copy { |
|
| 169 | 169 | fields: *[RecordField], |
|
| 170 | 170 | labeled: bool, |
|
| 171 | 171 | /// Cached layout. |
|
| 172 | 172 | layout: Layout, |
|
| 173 | 173 | /// Whether the declaration explicitly carries the `Linear` marker. |
|
| 174 | 174 | declaredLinear: bool, |
|
| 175 | + | /// Whether the declaration explicitly carries the `Copy` marker. |
|
| 176 | + | declaredCopy: bool, |
|
| 175 | 177 | } |
|
| 176 | 178 | ||
| 177 | 179 | /// Union nominal type. |
|
| 178 | - | export record UnionType { |
|
| 180 | + | export record UnionType: Copy { |
|
| 179 | 181 | variants: *[UnionVariant], |
|
| 180 | 182 | /// Cached layout. |
|
| 181 | 183 | layout: Layout, |
|
| 182 | 184 | /// Cached payload offset within the union aggregate. |
|
| 183 | 185 | valOffset: u32, |
|
| 184 | 186 | /// If all variants have void payloads. |
|
| 185 | 187 | isAllVoid: bool, |
|
| 186 | 188 | /// Whether the declaration explicitly carries the `Linear` marker. |
|
| 187 | 189 | declaredLinear: bool, |
|
| 190 | + | /// Whether the declaration explicitly carries the `Copy` marker. |
|
| 191 | + | declaredCopy: bool, |
|
| 188 | 192 | } |
|
| 189 | 193 | ||
| 190 | 194 | /// Metadata for user-defined types. |
|
| 191 | - | export union NominalType { |
|
| 195 | + | export union NominalType: Copy { |
|
| 192 | 196 | /// Placeholder for a type that hasn't been fully resolved yet. |
|
| 193 | 197 | /// Stores the declaration node for lazy resolution. |
|
| 194 | 198 | Placeholder(*ast::Node), |
|
| 195 | 199 | Record(RecordType), |
|
| 196 | 200 | Union(UnionType), |
|
| 197 | 201 | } |
|
| 198 | 202 | ||
| 199 | 203 | /// Coercion plan, when coercion from one type to another. |
|
| 200 | - | export union Coercion { |
|
| 204 | + | export union Coercion: Copy { |
|
| 201 | 205 | /// No coercion, eg. `T -> T`. |
|
| 202 | 206 | Identity, |
|
| 203 | 207 | /// Eg. `u8 -> i32`. Stores both source and target types for lowering. |
|
| 204 | 208 | NumericCast { from: Type, to: Type }, |
|
| 205 | 209 | /// Eg. `T -> ?T`. Stores the inner value type. |
| 214 | 218 | inst: *InstanceEntry, |
|
| 215 | 219 | }, |
|
| 216 | 220 | } |
|
| 217 | 221 | ||
| 218 | 222 | /// Result of resolving a module path. |
|
| 219 | - | record ResolvedModule { |
|
| 223 | + | record ResolvedModule: Copy { |
|
| 220 | 224 | /// Module entry in the graph. |
|
| 221 | 225 | entry: *module::ModuleEntry, |
|
| 222 | 226 | /// Scope containing the module's declarations. |
|
| 223 | 227 | scope: *mut Scope, |
|
| 224 | 228 | } |
|
| 225 | 229 | ||
| 226 | 230 | /// Type layout. |
|
| 227 | - | export record Layout { |
|
| 231 | + | export record Layout: Copy { |
|
| 228 | 232 | /// Size in bytes. |
|
| 229 | 233 | size: u32, |
|
| 230 | 234 | /// Alignment in bytes. |
|
| 231 | 235 | alignment: u32, |
|
| 232 | 236 | } |
|
| 233 | 237 | ||
| 234 | 238 | /// Computed union layout parameters. |
|
| 235 | - | record UnionLayoutInfo { |
|
| 239 | + | record UnionLayoutInfo: Copy { |
|
| 236 | 240 | layout: Layout, |
|
| 237 | 241 | valOffset: u32, |
|
| 238 | 242 | isAllVoid: bool, |
|
| 239 | 243 | } |
|
| 240 | 244 | ||
| 241 | 245 | /// Pre-computed metadata for slice range expressions. |
|
| 242 | 246 | /// Used by the lowerer. |
|
| 243 | - | export record SliceRangeInfo { |
|
| 247 | + | export record SliceRangeInfo: Copy { |
|
| 244 | 248 | /// Element type of the resulting slice. |
|
| 245 | 249 | itemType: *Type, |
|
| 246 | 250 | /// Whether the resulting slice is mutable. |
|
| 247 | 251 | mutable: bool, |
|
| 248 | 252 | /// Static capacity if container is an array. |
|
| 249 | 253 | capacity: ?u32, |
|
| 250 | 254 | } |
|
| 251 | 255 | ||
| 252 | 256 | /// Pre-computed metadata for `for` loop iteration. |
|
| 253 | 257 | /// Used by the lowerer to avoid re-analyzing the iterable type. |
|
| 254 | - | export union ForLoopInfo { |
|
| 258 | + | export union ForLoopInfo: Copy { |
|
| 255 | 259 | /// Iterating over a range expression (e.g., `for i in 0..n`). |
|
| 256 | 260 | Range { |
|
| 257 | 261 | valType: *Type, |
|
| 258 | 262 | range: ast::Range, |
|
| 259 | 263 | bindingName: ?*[u8], |
| 267 | 271 | indexName: ?*[u8] |
|
| 268 | 272 | }, |
|
| 269 | 273 | } |
|
| 270 | 274 | ||
| 271 | 275 | /// Resolved function signature details. |
|
| 272 | - | export record FnType { |
|
| 276 | + | export record FnType: Copy { |
|
| 273 | 277 | paramTypes: *[*Type], |
|
| 274 | 278 | returnType: *Type, |
|
| 275 | 279 | throwList: *[*Type], |
|
| 276 | 280 | /// Whether calling this function requires an unsafe context. |
|
| 277 | 281 | isUnsafe: bool, |
|
| 278 | 282 | localCount: u32, |
|
| 279 | 283 | } |
|
| 280 | 284 | ||
| 281 | 285 | /// Describes a type computed during semantic analysis. |
|
| 282 | - | export union Type { |
|
| 286 | + | export union Type: Copy { |
|
| 283 | 287 | /// A type that couldn't be decided. |
|
| 284 | 288 | Unknown, |
|
| 285 | 289 | /// Types only used during inference. |
|
| 286 | 290 | Nil, Undefined, Int, |
|
| 287 | 291 | /// Primitive types. |
| 323 | 327 | mutable: bool, |
|
| 324 | 328 | }, |
|
| 325 | 329 | } |
|
| 326 | 330 | ||
| 327 | 331 | /// Structured diagnostic payload for type mismatches. |
|
| 328 | - | export record TypeMismatch { |
|
| 332 | + | export record TypeMismatch: Copy { |
|
| 329 | 333 | expected: Type, |
|
| 330 | 334 | actual: Type, |
|
| 331 | 335 | } |
|
| 332 | 336 | ||
| 333 | 337 | /// Structured diagnostic payload for invalid `as` casts. |
|
| 334 | - | export record InvalidAsCast { |
|
| 338 | + | export record InvalidAsCast: Copy { |
|
| 335 | 339 | from: Type, |
|
| 336 | 340 | to: Type, |
|
| 337 | 341 | } |
|
| 338 | 342 | ||
| 339 | 343 | /// Diagnostic payload for argument count mismatches. |
|
| 340 | - | export record CountMismatch { |
|
| 344 | + | export record CountMismatch: Copy { |
|
| 341 | 345 | expected: u32, |
|
| 342 | 346 | actual: u32, |
|
| 343 | 347 | } |
|
| 344 | 348 | ||
| 345 | 349 | /// Detailed payload attached to a symbol, specialized per symbol kind. |
|
| 346 | - | export union SymbolData { |
|
| 350 | + | export union SymbolData: Copy { |
|
| 347 | 351 | /// Payload describing mutable bindings like variables or functions. |
|
| 348 | 352 | Value { |
|
| 349 | 353 | /// Whether the binding permits mutation. |
|
| 350 | 354 | mutable: bool, |
|
| 351 | 355 | /// Custom alignment requirement, or 0 for default. |
| 386 | 390 | /// Trait symbol. |
|
| 387 | 391 | Trait(*mut TraitType), |
|
| 388 | 392 | } |
|
| 389 | 393 | ||
| 390 | 394 | /// Resolved symbol allocated during semantic analysis. |
|
| 391 | - | export record Symbol { |
|
| 395 | + | export record Symbol: Copy { |
|
| 392 | 396 | /// Symbol name in source code. |
|
| 393 | 397 | name: *[u8], |
|
| 394 | 398 | /// Data associated with the symbol. |
|
| 395 | 399 | data: SymbolData, |
|
| 396 | 400 | /// Bitset of attributes applied to the declaration. |
| 400 | 404 | /// Module ID this symbol belongs to. Only for module-level symbols. |
|
| 401 | 405 | moduleId: ?u16, |
|
| 402 | 406 | } |
|
| 403 | 407 | ||
| 404 | 408 | /// Integer constant payload. |
|
| 405 | - | export record ConstInt { |
|
| 409 | + | export record ConstInt: Copy { |
|
| 406 | 410 | /// Absolute magnitude of the value. |
|
| 407 | 411 | magnitude: u64, |
|
| 408 | 412 | /// Bit width of the integer. |
|
| 409 | 413 | bits: u8, |
|
| 410 | 414 | /// Whether the integer is signed. |
| 412 | 416 | /// Whether the value is negative (only valid when `signed` is true). |
|
| 413 | 417 | negative: bool, |
|
| 414 | 418 | } |
|
| 415 | 419 | ||
| 416 | 420 | /// Constant value recorded for literal nodes. |
|
| 417 | - | export union ConstValue { |
|
| 421 | + | export union ConstValue: Copy { |
|
| 418 | 422 | Bool(bool), |
|
| 419 | 423 | Char(u8), |
|
| 420 | 424 | String(*[u8]), |
|
| 421 | 425 | Int(ConstInt), |
|
| 422 | 426 | } |
|
| 423 | 427 | ||
| 424 | 428 | /// Integer range metadata for primitive integer types. |
|
| 425 | - | union IntegerRange { |
|
| 429 | + | union IntegerRange: Copy { |
|
| 426 | 430 | Signed { |
|
| 427 | 431 | bits: u8, |
|
| 428 | 432 | min: i64, |
|
| 429 | 433 | max: i64, |
|
| 430 | 434 | lim: u64, |
| 434 | 438 | max: u64, |
|
| 435 | 439 | }, |
|
| 436 | 440 | } |
|
| 437 | 441 | ||
| 438 | 442 | /// Diagnostic emitted by the analyzer. |
|
| 439 | - | export record Error { |
|
| 443 | + | export record Error: Copy { |
|
| 440 | 444 | /// Error category. |
|
| 441 | 445 | kind: ErrorKind, |
|
| 442 | 446 | /// Node associated with the error, if known. |
|
| 443 | 447 | node: ?*ast::Node, |
|
| 444 | 448 | /// Module ID where this error occurred. |
|
| 445 | 449 | moduleId: u16, |
|
| 446 | 450 | } |
|
| 447 | 451 | ||
| 448 | 452 | /// High-level classification for semantic diagnostics. |
|
| 449 | - | export union ErrorKind { |
|
| 453 | + | export union ErrorKind: Copy { |
|
| 450 | 454 | /// Identifier declared more than once in the same scope. |
|
| 451 | 455 | DuplicateBinding(*[u8]), |
|
| 452 | 456 | /// Identifier referenced before it was declared. |
|
| 453 | 457 | UnresolvedSymbol(*[u8]), |
|
| 454 | 458 | /// Attempted to assign to an immutable binding. |
| 593 | 597 | FnThrowOverflow(CountMismatch), |
|
| 594 | 598 | /// Trait declaration has too many methods. |
|
| 595 | 599 | TraitMethodOverflow(CountMismatch), |
|
| 596 | 600 | /// Instance declaration is missing a required supertrait instance. |
|
| 597 | 601 | MissingSupertraitInstance(*[u8]), |
|
| 602 | + | /// An affine binding was used after it moved. |
|
| 603 | + | AffineUseAfterMove(*[u8]), |
|
| 598 | 604 | /// Linear binding was consumed more than once. |
|
| 599 | 605 | LinearUseAfterConsume(*[u8]), |
|
| 600 | 606 | /// Linear binding remains available at an exit. |
|
| 601 | 607 | LinearNotConsumed(*[u8]), |
|
| 602 | 608 | /// A case-pattern `let-else` fallback must terminate control flow. |
| 609 | 615 | LinearDiscard, |
|
| 610 | 616 | /// Assignment would overwrite a live linear value. |
|
| 611 | 617 | LinearOverwrite, |
|
| 612 | 618 | /// `undefined` cannot initialize a linear type. |
|
| 613 | 619 | LinearUndefined, |
|
| 620 | + | /// A `Copy` declaration contains a non-copy field or variant. |
|
| 621 | + | CopyContainsNonCopy, |
|
| 622 | + | /// A declaration carries both `Copy` and `Linear`. |
|
| 623 | + | ConflictingOwnershipMarkers, |
|
| 614 | 624 | /// A reference appears in a storable or escaping position. |
|
| 615 | 625 | InvalidRefPosition, |
|
| 616 | 626 | /// A reference cannot be bound to a local. |
|
| 617 | 627 | RefBinding, |
|
| 618 | 628 | /// Call arguments contain overlapping incompatible loans. |
| 624 | 634 | /// Internal error. |
|
| 625 | 635 | Internal, |
|
| 626 | 636 | } |
|
| 627 | 637 | ||
| 628 | 638 | /// Diagnostics returned by the analyzer. |
|
| 629 | - | export record Diagnostics { |
|
| 639 | + | export record Diagnostics: Copy { |
|
| 630 | 640 | errors: *mut [Error], |
|
| 631 | 641 | } |
|
| 632 | 642 | ||
| 633 | 643 | /// Call context. |
|
| 634 | - | union CallCtx { |
|
| 644 | + | union CallCtx: Copy { |
|
| 635 | 645 | /// Normal function call. |
|
| 636 | 646 | Normal, |
|
| 637 | 647 | /// Fallible function call, ie. `try f()`. |
|
| 638 | 648 | Try, |
|
| 639 | 649 | } |
|
| 640 | 650 | ||
| 641 | 651 | /// Result of resolving a record literal's type name. |
|
| 642 | - | record ResolvedRecordLitType { |
|
| 652 | + | record ResolvedRecordLitType: Copy { |
|
| 643 | 653 | /// The record nominal type to use for field checking. |
|
| 644 | 654 | recordType: *NominalType, |
|
| 645 | 655 | /// The result type of the literal (record type or union type for variants). |
|
| 646 | 656 | resultType: Type, |
|
| 647 | 657 | } |
|
| 648 | 658 | ||
| 649 | 659 | /// Result of checking for a `super` path prefix. |
|
| 650 | - | record SuperAccessResult { |
|
| 660 | + | record SuperAccessResult: Copy { |
|
| 651 | 661 | scope: *mut Scope, |
|
| 652 | 662 | child: *ast::Node, |
|
| 653 | 663 | } |
|
| 654 | 664 | ||
| 655 | 665 | /// Node-specific resolver metadata. |
|
| 656 | - | export union NodeExtra { |
|
| 666 | + | export union NodeExtra: Copy { |
|
| 657 | 667 | /// No extra data for this node. |
|
| 658 | 668 | None, |
|
| 659 | 669 | /// Resolved field index for record literal fields. |
|
| 660 | 670 | RecordField { index: u32 }, |
|
| 661 | 671 | /// Slice range metadata for subscript expressions with ranges. |
| 682 | 692 | /// Slice `.delete(index)` method call. |
|
| 683 | 693 | SliceDelete { elemType: *Type }, |
|
| 684 | 694 | } |
|
| 685 | 695 | ||
| 686 | 696 | /// Combined resolver metadata for a single AST node. |
|
| 687 | - | export record NodeData { |
|
| 697 | + | export record NodeData: Copy { |
|
| 688 | 698 | /// Resolved type for this node. |
|
| 689 | 699 | ty: Type, |
|
| 690 | 700 | /// Coercion plan applied to this node. |
|
| 691 | 701 | coercion: Coercion, |
|
| 692 | 702 | /// Symbol associated with this node. |
| 698 | 708 | /// Node-specific extra data. |
|
| 699 | 709 | extra: NodeExtra, |
|
| 700 | 710 | } |
|
| 701 | 711 | ||
| 702 | 712 | /// Table storing all resolver metadata indexed by node ID. |
|
| 703 | - | record NodeDataTable { |
|
| 713 | + | record NodeDataTable: Copy { |
|
| 704 | 714 | entries: *mut [NodeData], |
|
| 705 | 715 | } |
|
| 706 | 716 | ||
| 707 | 717 | /// Lexical scope. |
|
| 708 | - | export record Scope { |
|
| 718 | + | export record Scope: Copy { |
|
| 709 | 719 | /// Owning AST node, or `nil` for the root scope. |
|
| 710 | 720 | owner: ?*ast::Node, |
|
| 711 | 721 | /// Parent/enclosing scope. |
|
| 712 | 722 | parent: ?*mut Scope, |
|
| 713 | 723 | /// Module ID if this is a module scope. |
| 717 | 727 | /// Number of live symbols. |
|
| 718 | 728 | symbolsLen: u32, |
|
| 719 | 729 | } |
|
| 720 | 730 | ||
| 721 | 731 | /// An object used by the enter and exit functions for module scopes. |
|
| 722 | - | record ModuleScope { |
|
| 732 | + | record ModuleScope: Copy { |
|
| 723 | 733 | /// Module root node. |
|
| 724 | 734 | root: *ast::Node, |
|
| 725 | 735 | /// Module entry in graph. |
|
| 726 | 736 | entry: *module::ModuleEntry, |
|
| 727 | 737 | /// The newly entered scope. |
| 731 | 741 | /// The previous module. |
|
| 732 | 742 | prevMod: u16, |
|
| 733 | 743 | } |
|
| 734 | 744 | ||
| 735 | 745 | /// Loop context for tracking control flow within loops. |
|
| 736 | - | record LoopCtx { |
|
| 746 | + | record LoopCtx: Copy { |
|
| 737 | 747 | /// Whether a reachable break was encountered in this loop. |
|
| 738 | 748 | /// This is used to determine whether a loop diverges. |
|
| 739 | 749 | hasBreak: bool, |
|
| 740 | 750 | } |
|
| 741 | 751 | ||
| 742 | 752 | /// Configuration for semantic analysis. |
|
| 743 | - | export record Config { |
|
| 753 | + | export record Config: Copy { |
|
| 744 | 754 | /// Whether we're building in test mode. |
|
| 745 | 755 | buildTest: bool, |
|
| 746 | 756 | } |
|
| 747 | 757 | ||
| 748 | 758 | /// How pattern bindings are created during match. |
|
| 749 | - | export union MatchBy { |
|
| 759 | + | export union MatchBy: Copy { |
|
| 750 | 760 | /// Match by value. |
|
| 751 | 761 | Value, |
|
| 752 | 762 | /// Match by immutable reference. |
|
| 753 | 763 | Ref, |
|
| 754 | 764 | /// Match by mutable reference. |
|
| 755 | 765 | MutRef, |
|
| 756 | 766 | } |
|
| 757 | 767 | ||
| 758 | 768 | /// State of a match statement being resolved. |
|
| 759 | 769 | // TODO: This is only used because of the maximum function param limitation. |
|
| 760 | - | record MatchState { |
|
| 770 | + | record MatchState: Copy { |
|
| 761 | 771 | /// Is the match catch-all? |
|
| 762 | 772 | catchAll: bool, |
|
| 763 | 773 | /// Is the match constant? |
|
| 764 | 774 | isConst: bool |
|
| 765 | 775 | } |
|
| 766 | 776 | ||
| 767 | 777 | /// Result of unwrapping a type for pattern matching. |
|
| 768 | - | export record MatchSubject { |
|
| 778 | + | export record MatchSubject: Copy { |
|
| 769 | 779 | /// The effective type to match against. |
|
| 770 | 780 | effectiveTy: Type, |
|
| 771 | 781 | /// How bindings should be created. |
|
| 772 | 782 | by: MatchBy, |
|
| 773 | 783 | } |
|
| 774 | 784 | ||
| 775 | 785 | /// How an expression uses a linear result. |
|
| 776 | - | union LinearUse { |
|
| 786 | + | union LinearUse: Copy { |
|
| 777 | 787 | /// Consume the value and end its availability. |
|
| 778 | 788 | Consume, |
|
| 779 | 789 | /// Read the value without consuming it. |
|
| 780 | 790 | Observe, |
|
| 781 | 791 | /// Borrow the value through a reference. |
| 785 | 795 | /// Use the value as an assignment target. |
|
| 786 | 796 | Place, |
|
| 787 | 797 | } |
|
| 788 | 798 | ||
| 789 | 799 | /// Per-control-flow-path ownership state. |
|
| 790 | - | record LinearEnv { |
|
| 800 | + | record LinearEnv: Copy { |
|
| 791 | 801 | /// Symbols tracked on this control-flow path. |
|
| 792 | 802 | symbols: [?*mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 793 | 803 | /// Bit set for each binding that remains available. |
|
| 794 | 804 | available: u64, |
|
| 795 | 805 | /// Number of entries in `symbols`. |
| 797 | 807 | /// Whether this control-flow path has terminated. |
|
| 798 | 808 | terminated: bool, |
|
| 799 | 809 | } |
|
| 800 | 810 | ||
| 801 | 811 | /// Function-local exact-use checker state. |
|
| 802 | - | record LinearChecker { |
|
| 812 | + | record LinearChecker: Copy { |
|
| 803 | 813 | /// Resolver that owns the symbols and diagnostics. |
|
| 804 | 814 | resolver: *mut Resolver, |
|
| 805 | 815 | /// Binding count at entry to each active loop. |
|
| 806 | 816 | loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH], |
|
| 807 | 817 | /// Available bindings at entry to each active loop. |
| 824 | 834 | } |
|
| 825 | 835 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 826 | 836 | } |
|
| 827 | 837 | ||
| 828 | 838 | /// Global resolver state. |
|
| 829 | - | export record Resolver { |
|
| 839 | + | export record Resolver: Copy { |
|
| 830 | 840 | /// Current scope. |
|
| 831 | 841 | scope: *mut Scope, |
|
| 832 | 842 | /// Package scope containing package roots and top-level symbols. |
|
| 833 | 843 | pkgScope: *mut Scope, |
|
| 834 | 844 | /// Stack of loop contexts for nested loops. |
| 864 | 874 | /// Number of registered standalone methods. |
|
| 865 | 875 | methodsLen: u32, |
|
| 866 | 876 | } |
|
| 867 | 877 | ||
| 868 | 878 | /// Internal error sentinel thrown when analysis cannot proceed. |
|
| 869 | - | export union ResolveError { |
|
| 879 | + | export union ResolveError: Copy { |
|
| 870 | 880 | Failure, |
|
| 871 | 881 | } |
|
| 872 | 882 | ||
| 873 | 883 | /// Node in the type interning linked list. |
|
| 874 | - | record TypeNode { |
|
| 884 | + | record TypeNode: Copy { |
|
| 875 | 885 | ty: Type, |
|
| 876 | 886 | next: ?*TypeNode, |
|
| 877 | 887 | } |
|
| 878 | 888 | ||
| 879 | 889 | /// Allocate and intern a type in the arena, returning a pointer for deduplication. |
| 932 | 942 | } |
|
| 933 | 943 | return nil; |
|
| 934 | 944 | } |
|
| 935 | 945 | ||
| 936 | 946 | /// Storage buffers used by the analyzer. |
|
| 937 | - | export record ResolverStorage { |
|
| 947 | + | export record ResolverStorage: Copy { |
|
| 938 | 948 | /// Unified arena for symbols, scopes, and nominal type. |
|
| 939 | 949 | arena: alloc::Arena, |
|
| 940 | 950 | /// Node semantic metadata indexed by node ID. |
|
| 941 | 951 | nodeData: *mut [NodeData], |
|
| 942 | 952 | /// Package scope. |
| 944 | 954 | /// Error storage. |
|
| 945 | 955 | errors: *mut [Error], |
|
| 946 | 956 | } |
|
| 947 | 957 | ||
| 948 | 958 | /// Input for resolving a single package. |
|
| 949 | - | export record Pkg { |
|
| 959 | + | export record Pkg: Copy { |
|
| 950 | 960 | /// Root module entry. |
|
| 951 | 961 | rootEntry: *module::ModuleEntry, |
|
| 952 | 962 | /// Root AST node. |
|
| 953 | 963 | rootAst: *ast::Node, |
|
| 954 | 964 | } |
| 2025 | 2035 | case Type::Nominal(_) => return false, |
|
| 2026 | 2036 | else => return false, |
|
| 2027 | 2037 | } |
|
| 2028 | 2038 | } |
|
| 2029 | 2039 | ||
| 2030 | - | /// Return whether a type is exact-linear. |
|
| 2031 | - | export fn isLinear(ty: Type) -> bool { |
|
| 2040 | + | /// Return whether `ty` may be duplicated implicitly. |
|
| 2041 | + | export fn isCopy(ty: Type) -> bool { |
|
| 2032 | 2042 | match ty { |
|
| 2033 | - | case Type::Pointer { class: types::PointerClass::Owned, .. }, |
|
| 2034 | - | Type::Slice { class: types::PointerClass::Owned, .. }, |
|
| 2035 | - | Type::TraitObject { class: types::PointerClass::Owned, .. } => return true, |
|
| 2036 | - | case Type::Pointer { class: types::PointerClass::Ref, .. }, |
|
| 2037 | - | Type::Pointer { class: types::PointerClass::Unsafe, .. }, |
|
| 2038 | - | Type::Slice { class: types::PointerClass::Ref, .. }, |
|
| 2039 | - | Type::Slice { class: types::PointerClass::Unsafe, .. }, |
|
| 2040 | - | Type::TraitObject { class: types::PointerClass::Ref, .. }, |
|
| 2041 | - | Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return false, |
|
| 2043 | + | case Type::Array(array) => return isCopy(*array.item), |
|
| 2044 | + | case Type::Optional(inner) => return isCopy(*inner), |
|
| 2045 | + | case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy, |
|
| 2046 | + | case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy, |
|
| 2047 | + | case Type::Nominal(NominalType::Placeholder(_)) => return false, |
|
| 2048 | + | else => return true, |
|
| 2049 | + | } |
|
| 2050 | + | } |
|
| 2042 | 2051 | ||
| 2052 | + | /// Return whether a type must be consumed exactly once. |
|
| 2053 | + | export fn isLinear(ty: Type) -> bool { |
|
| 2054 | + | match ty { |
|
| 2043 | 2055 | case Type::Array(array) => return isLinear(*array.item), |
|
| 2044 | 2056 | case Type::Optional(inner) => return isLinear(*inner), |
|
| 2045 | 2057 | case Type::Nominal(NominalType::Record(recInfo)) => { |
|
| 2046 | 2058 | if recInfo.declaredLinear { |
|
| 2047 | 2059 | return true; |
| 2066 | 2078 | } |
|
| 2067 | 2079 | else => return false, |
|
| 2068 | 2080 | } |
|
| 2069 | 2081 | } |
|
| 2070 | 2082 | ||
| 2083 | + | /// Return whether a by-value use moves `ty`. |
|
| 2084 | + | fn isMoveOnly(ty: Type) -> bool { |
|
| 2085 | + | return not isCopy(ty); |
|
| 2086 | + | } |
|
| 2087 | + | ||
| 2071 | 2088 | /// Return whether `ty` is a direct unsafe pointer-like value. |
|
| 2072 | 2089 | fn isUnsafePointerType(ty: Type) -> bool { |
|
| 2073 | 2090 | match ty { |
|
| 2074 | 2091 | case Type::Pointer { class: types::PointerClass::Unsafe, .. }, |
|
| 2075 | 2092 | Type::Slice { class: types::PointerClass::Unsafe, .. }, |
| 3406 | 3423 | if retTy <> Type::Void and bodyTy <> Type::Never { |
|
| 3407 | 3424 | exitFn(self); |
|
| 3408 | 3425 | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 3409 | 3426 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 3410 | 3427 | } |
|
| 3428 | + | try checkLinearFn(self, nil, decl.sig.params, body) catch e { |
|
| 3429 | + | exitFn(self); |
|
| 3430 | + | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 3431 | + | throw e; |
|
| 3432 | + | }; |
|
| 3411 | 3433 | exitFn(self); |
|
| 3412 | 3434 | if isUnsafe { |
|
| 3413 | 3435 | set self.unsafeDepth -= 1; |
|
| 3414 | 3436 | } |
|
| 3415 | 3437 | } else if not isExtern { |
| 3425 | 3447 | let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0); |
|
| 3426 | 3448 | ||
| 3427 | 3449 | return ty; |
|
| 3428 | 3450 | } |
|
| 3429 | 3451 | ||
| 3430 | - | /// Resolve the compiler-known `Linear` marker from a derive list. |
|
| 3431 | - | fn resolveLinearDerive(self: *mut Resolver, derives: *mut [*ast::Node]) -> bool |
|
| 3452 | + | /// Compiler-known ownership markers carried by a composite declaration. |
|
| 3453 | + | record OwnershipMarkers: Copy { |
|
| 3454 | + | /// The declaration requires exact consumption. |
|
| 3455 | + | linear: bool, |
|
| 3456 | + | /// The declaration permits implicit copies. |
|
| 3457 | + | copy: bool, |
|
| 3458 | + | } |
|
| 3459 | + | ||
| 3460 | + | /// Resolve compiler-known ownership markers from a derive list. |
|
| 3461 | + | fn resolveOwnershipMarkers(self: *mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers |
|
| 3432 | 3462 | throws (ResolveError) |
|
| 3433 | 3463 | { |
|
| 3434 | - | let mut linear = false; |
|
| 3464 | + | let mut result = OwnershipMarkers { linear: false, copy: false }; |
|
| 3435 | 3465 | for derive in derives { |
|
| 3436 | 3466 | let name = try nodeName(self, derive); |
|
| 3437 | 3467 | if mem::eq(name, "Linear") { |
|
| 3438 | - | if linear { |
|
| 3468 | + | if result.linear { |
|
| 3469 | + | throw emitError(self, derive, ErrorKind::DuplicateBinding(name)); |
|
| 3470 | + | } |
|
| 3471 | + | if result.copy { |
|
| 3472 | + | throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers); |
|
| 3473 | + | } |
|
| 3474 | + | set result.linear = true; |
|
| 3475 | + | } else if mem::eq(name, "Copy") { |
|
| 3476 | + | if result.copy { |
|
| 3439 | 3477 | throw emitError(self, derive, ErrorKind::DuplicateBinding(name)); |
|
| 3440 | 3478 | } |
|
| 3441 | - | set linear = true; |
|
| 3479 | + | if result.linear { |
|
| 3480 | + | throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers); |
|
| 3481 | + | } |
|
| 3482 | + | set result.copy = true; |
|
| 3442 | 3483 | } else { |
|
| 3443 | 3484 | // Resolve an ordinary trait derive. |
|
| 3444 | 3485 | try infer(self, derive); |
|
| 3445 | 3486 | } |
|
| 3446 | 3487 | } |
|
| 3447 | - | return linear; |
|
| 3488 | + | return result; |
|
| 3448 | 3489 | } |
|
| 3449 | 3490 | ||
| 3450 | 3491 | /// Resolve record fields from a node list. |
|
| 3451 | 3492 | fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType |
|
| 3452 | 3493 | throws (ResolveError) |
| 3505 | 3546 | return RecordType { |
|
| 3506 | 3547 | fields: &result[..], |
|
| 3507 | 3548 | labeled, |
|
| 3508 | 3549 | layout: recordLayout, |
|
| 3509 | 3550 | declaredLinear: false, |
|
| 3551 | + | declaredCopy: false, |
|
| 3510 | 3552 | }; |
|
| 3511 | 3553 | } |
|
| 3512 | 3554 | ||
| 3513 | 3555 | /// Resolve record field types for a named record declaration. |
|
| 3514 | 3556 | fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl) |
| 3523 | 3565 | ||
| 3524 | 3566 | // Skip if already resolved. |
|
| 3525 | 3567 | if let case NominalType::Record(_) = *nominalTy { |
|
| 3526 | 3568 | return; |
|
| 3527 | 3569 | } |
|
| 3528 | - | let declaredLinear = try resolveLinearDerive(self, decl.derives); |
|
| 3570 | + | let markers = try resolveOwnershipMarkers(self, decl.derives); |
|
| 3529 | 3571 | let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled); |
|
| 3530 | - | set recordType.declaredLinear = declaredLinear; |
|
| 3572 | + | if markers.copy { |
|
| 3573 | + | for field in recordType.fields { |
|
| 3574 | + | if not isCopy(field.fieldType) { |
|
| 3575 | + | throw emitError(self, node, ErrorKind::CopyContainsNonCopy); |
|
| 3576 | + | } |
|
| 3577 | + | } |
|
| 3578 | + | } |
|
| 3579 | + | set recordType.declaredLinear = markers.linear; |
|
| 3580 | + | set recordType.declaredCopy = markers.copy; |
|
| 3531 | 3581 | ||
| 3532 | 3582 | set *nominalTy = NominalType::Record(recordType); |
|
| 3533 | 3583 | } |
|
| 3534 | 3584 | ||
| 3535 | 3585 | /// Bind a type name. |
| 4011 | 4061 | if retTy <> Type::Void and bodyTy <> Type::Never { |
|
| 4012 | 4062 | exitFn(self); |
|
| 4013 | 4063 | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4014 | 4064 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 4015 | 4065 | } |
|
| 4066 | + | try checkLinearFn(self, receiverName, sig.params, body) catch e { |
|
| 4067 | + | exitFn(self); |
|
| 4068 | + | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4069 | + | throw e; |
|
| 4070 | + | }; |
|
| 4016 | 4071 | exitFn(self); |
|
| 4017 | 4072 | if isUnsafe { |
|
| 4018 | 4073 | set self.unsafeDepth -= 1; |
|
| 4019 | 4074 | } |
|
| 4020 | 4075 | } |
| 4203 | 4258 | let mut variants: *mut [UnionVariant] = &mut []; |
|
| 4204 | 4259 | ||
| 4205 | 4260 | // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion |
|
| 4206 | 4261 | // when a variant references this union type (e.g. record payloads with `*[Self]`). |
|
| 4207 | 4262 | // TODO: It would be best to have a resolving state eg. `Visiting` for this situation. |
|
| 4208 | - | let declaredLinear = try resolveLinearDerive(self, decl.derives); |
|
| 4263 | + | let markers = try resolveOwnershipMarkers(self, decl.derives); |
|
| 4209 | 4264 | set *nominalTy = NominalType::Union(UnionType { |
|
| 4210 | 4265 | variants: &[], |
|
| 4211 | 4266 | layout: Layout { size: 0, alignment: 0 }, |
|
| 4212 | 4267 | valOffset: 0, |
|
| 4213 | 4268 | isAllVoid: true, |
|
| 4214 | - | declaredLinear, |
|
| 4269 | + | declaredLinear: markers.linear, |
|
| 4270 | + | declaredCopy: markers.copy, |
|
| 4215 | 4271 | }); |
|
| 4216 | 4272 | ||
| 4217 | 4273 | assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded"; |
|
| 4218 | 4274 | let mut iota: u32 = 0; |
|
| 4219 | 4275 | for variantNode, i in decl.variants { |
| 4237 | 4293 | name: variantName, |
|
| 4238 | 4294 | valueType: variantType, |
|
| 4239 | 4295 | symbol: variantSym, |
|
| 4240 | 4296 | }, a); |
|
| 4241 | 4297 | } |
|
| 4298 | + | if markers.copy { |
|
| 4299 | + | for variant in variants { |
|
| 4300 | + | if not isCopy(variant.valueType) { |
|
| 4301 | + | throw emitError(self, node, ErrorKind::CopyContainsNonCopy); |
|
| 4302 | + | } |
|
| 4303 | + | } |
|
| 4304 | + | } |
|
| 4242 | 4305 | let info = computeUnionLayout(&variants[..]); |
|
| 4243 | 4306 | ||
| 4244 | 4307 | // Update the nominal type with the resolved variants. |
|
| 4245 | 4308 | set *nominalTy = NominalType::Union(UnionType { |
|
| 4246 | 4309 | variants: &variants[..], |
|
| 4247 | 4310 | layout: info.layout, |
|
| 4248 | 4311 | valOffset: info.valOffset, |
|
| 4249 | 4312 | isAllVoid: info.isAllVoid, |
|
| 4250 | - | declaredLinear, |
|
| 4313 | + | declaredLinear: markers.linear, |
|
| 4314 | + | declaredCopy: markers.copy, |
|
| 4251 | 4315 | }); |
|
| 4252 | 4316 | } |
|
| 4253 | 4317 | ||
| 4254 | 4318 | /// Check if a module should be analyzed based on its attributes and build configuration. |
|
| 4255 | 4319 | fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool { |
| 4400 | 4464 | ||
| 4401 | 4465 | return setNodeType(self, node, unifyBranches(thenTy, elseTy)); |
|
| 4402 | 4466 | } |
|
| 4403 | 4467 | ||
| 4404 | 4468 | /// Controls how bare identifiers are handled in case patterns. |
|
| 4405 | - | union IdentMode { |
|
| 4469 | + | union IdentMode: Copy { |
|
| 4406 | 4470 | /// Identifier is a value to compare against. |
|
| 4407 | 4471 | Compare, |
|
| 4408 | 4472 | /// Identifier introduces a new binding. |
|
| 4409 | 4473 | Bind, |
|
| 4410 | 4474 | } |
| 7131 | 7195 | case ast::TypeSig::Nominal(name) => { |
|
| 7132 | 7196 | let ty = try resolveTypeName(self, name); |
|
| 7133 | 7197 | return Type::Nominal(ty); |
|
| 7134 | 7198 | } |
|
| 7135 | 7199 | case ast::TypeSig::Record { fields, labeled } => { |
|
| 7136 | - | let recordType = try resolveRecordFields(self, node, fields, labeled); |
|
| 7200 | + | let mut recordType = try resolveRecordFields(self, node, fields, labeled); |
|
| 7201 | + | set recordType.declaredCopy = true; |
|
| 7202 | + | for field in recordType.fields { |
|
| 7203 | + | if not isCopy(field.fieldType) { |
|
| 7204 | + | set recordType.declaredCopy = false; |
|
| 7205 | + | } |
|
| 7206 | + | } |
|
| 7137 | 7207 | let nominalTy = allocNominalType(self, NominalType::Record(recordType)); |
|
| 7138 | 7208 | return Type::Nominal(nominalTy); |
|
| 7139 | 7209 | } |
|
| 7140 | 7210 | case ast::TypeSig::Fn(t) => { |
|
| 7141 | 7211 | let a = alloc::arenaAllocator(&mut self.arena); |
| 7380 | 7450 | /// Return whether a tracked binding is still available. |
|
| 7381 | 7451 | fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool { |
|
| 7382 | 7452 | return (env.available & ((1 as u64) << (index as u64))) <> 0; |
|
| 7383 | 7453 | } |
|
| 7384 | 7454 | ||
| 7385 | - | /// Add a local binding when its resolved type is linear. |
|
| 7455 | + | /// Add a local binding when its resolved type moves by value. |
|
| 7386 | 7456 | fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node) |
|
| 7387 | 7457 | throws (ResolveError) |
|
| 7388 | 7458 | { |
|
| 7389 | 7459 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7390 | 7460 | let case SymbolData::Value { type: ty, .. } = sym.data else return; |
|
| 7391 | - | if not isLinear(ty) { |
|
| 7461 | + | if not isMoveOnly(ty) { |
|
| 7392 | 7462 | return; |
|
| 7393 | 7463 | } |
|
| 7394 | 7464 | if env.len >= MAX_LINEAR_BINDINGS { |
|
| 7395 | 7465 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 7396 | 7466 | } |
|
| 7397 | 7467 | set env.symbols[env.len] = sym; |
|
| 7398 | 7468 | set env.available |= (1 as u64) << (env.len as u64); |
|
| 7399 | 7469 | set env.len += 1; |
|
| 7400 | 7470 | } |
|
| 7401 | 7471 | ||
| 7402 | - | /// Require all bindings introduced after `start` to have been consumed. |
|
| 7472 | + | /// Mark a tracked binding as uninitialized. |
|
| 7473 | + | fn markLinearBindingUnavailable(self: *mut Resolver, env: *mut LinearEnv, node: *ast::Node) { |
|
| 7474 | + | let sym = symbolFor(self, node) else return; |
|
| 7475 | + | let index = findLinearBinding(env, sym) else return; |
|
| 7476 | + | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7477 | + | } |
|
| 7478 | + | ||
| 7479 | + | /// Require exact-use bindings introduced after `start` to be consumed. |
|
| 7403 | 7480 | fn finishLinearScope( |
|
| 7404 | 7481 | checker: *mut LinearChecker, |
|
| 7405 | 7482 | env: *mut LinearEnv, |
|
| 7406 | 7483 | start: u32, |
|
| 7407 | 7484 | ) throws (ResolveError) { |
|
| 7408 | 7485 | if not env.terminated { |
|
| 7409 | 7486 | for i in start..env.len { |
|
| 7410 | 7487 | if linearBindingAvailable(env, i) { |
|
| 7411 | 7488 | let sym = env.symbols[i] else panic "finishLinearScope: missing symbol"; |
|
| 7412 | - | throw emitError( |
|
| 7413 | - | checker.resolver, |
|
| 7414 | - | sym.node, |
|
| 7415 | - | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7416 | - | ); |
|
| 7489 | + | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7490 | + | else panic "finishLinearScope: expected value symbol"; |
|
| 7491 | + | if isLinear(ty) { |
|
| 7492 | + | throw emitError( |
|
| 7493 | + | checker.resolver, |
|
| 7494 | + | sym.node, |
|
| 7495 | + | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7496 | + | ); |
|
| 7497 | + | } |
|
| 7417 | 7498 | } |
|
| 7418 | 7499 | } |
|
| 7419 | 7500 | } |
|
| 7420 | 7501 | set env.len = start; |
|
| 7421 | 7502 | } |
|
| 7422 | 7503 | ||
| 7423 | - | /// Consume a tracked identifier exactly once. |
|
| 7504 | + | /// Move or consume a tracked identifier once. |
|
| 7424 | 7505 | fn consumeLinearIdent( |
|
| 7425 | 7506 | checker: *mut LinearChecker, |
|
| 7426 | 7507 | env: *mut LinearEnv, |
|
| 7427 | 7508 | node: *ast::Node, |
|
| 7428 | 7509 | ) throws (ResolveError) { |
|
| 7429 | 7510 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7430 | 7511 | let index = findLinearBinding(env, sym) else return; |
|
| 7431 | 7512 | if not linearBindingAvailable(env, index) { |
|
| 7432 | - | throw emitError( |
|
| 7433 | - | checker.resolver, |
|
| 7434 | - | node, |
|
| 7435 | - | ErrorKind::LinearUseAfterConsume(sym.name), |
|
| 7436 | - | ); |
|
| 7513 | + | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7514 | + | else panic "consumeLinearIdent: expected value symbol"; |
|
| 7515 | + | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty) |
|
| 7516 | + | else ErrorKind::AffineUseAfterMove(sym.name); |
|
| 7517 | + | throw emitError(checker.resolver, node, kind); |
|
| 7437 | 7518 | } |
|
| 7438 | 7519 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7439 | 7520 | } |
|
| 7440 | 7521 | ||
| 7441 | - | /// Verify that two live branches agree on every outer binding. |
|
| 7522 | + | /// Merge ownership availability across two live branches. |
|
| 7442 | 7523 | fn joinLinearBranches( |
|
| 7443 | 7524 | checker: *mut LinearChecker, |
|
| 7444 | 7525 | env: *mut LinearEnv, |
|
| 7445 | 7526 | left: LinearEnv, |
|
| 7446 | 7527 | right: LinearEnv, |
| 7458 | 7539 | if right.terminated { |
|
| 7459 | 7540 | set *env = left; |
|
| 7460 | 7541 | return; |
|
| 7461 | 7542 | } |
|
| 7462 | 7543 | assert left.len == right.len, "joinLinearBranches: scope mismatch"; |
|
| 7544 | + | let mut result = left; |
|
| 7463 | 7545 | for i in 0..left.len { |
|
| 7464 | 7546 | if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) { |
|
| 7465 | 7547 | let sym = left.symbols[i] else panic "joinLinearBranches: missing symbol"; |
|
| 7466 | - | throw emitError( |
|
| 7467 | - | checker.resolver, |
|
| 7468 | - | node, |
|
| 7469 | - | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7470 | - | ); |
|
| 7548 | + | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7549 | + | else panic "joinLinearBranches: expected value symbol"; |
|
| 7550 | + | if isLinear(ty) { |
|
| 7551 | + | throw emitError( |
|
| 7552 | + | checker.resolver, |
|
| 7553 | + | node, |
|
| 7554 | + | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7555 | + | ); |
|
| 7556 | + | } |
|
| 7557 | + | set result.available &= ~((1 as u64) << (i as u64)); |
|
| 7471 | 7558 | } |
|
| 7472 | 7559 | } |
|
| 7473 | - | set *env = left; |
|
| 7560 | + | set *env = result; |
|
| 7474 | 7561 | } |
|
| 7475 | 7562 | ||
| 7476 | - | /// Require all current bindings to be consumed at a function exit. |
|
| 7563 | + | /// Require all available exact-use bindings to be consumed at a function exit. |
|
| 7477 | 7564 | fn finishLinearExit( |
|
| 7478 | 7565 | checker: *mut LinearChecker, |
|
| 7479 | 7566 | env: *mut LinearEnv, |
|
| 7480 | 7567 | ) throws (ResolveError) { |
|
| 7481 | 7568 | for i in 0..env.len { |
|
| 7482 | 7569 | if linearBindingAvailable(env, i) { |
|
| 7483 | 7570 | let sym = env.symbols[i] else panic "finishLinearExit: missing symbol"; |
|
| 7484 | - | throw emitError( |
|
| 7485 | - | checker.resolver, |
|
| 7486 | - | sym.node, |
|
| 7487 | - | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7488 | - | ); |
|
| 7571 | + | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7572 | + | else panic "finishLinearExit: expected value symbol"; |
|
| 7573 | + | if isLinear(ty) { |
|
| 7574 | + | throw emitError( |
|
| 7575 | + | checker.resolver, |
|
| 7576 | + | sym.node, |
|
| 7577 | + | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7578 | + | ); |
|
| 7579 | + | } |
|
| 7489 | 7580 | } |
|
| 7490 | 7581 | } |
|
| 7491 | 7582 | set env.terminated = true; |
|
| 7492 | 7583 | } |
|
| 7493 | 7584 |
| 7692 | 7783 | case ast::ProngArm::Binding(binding) => { |
|
| 7693 | 7784 | try addLinearPatternBindings(checker, &mut branch, binding); |
|
| 7694 | 7785 | } |
|
| 7695 | 7786 | case ast::ProngArm::Else => {} |
|
| 7696 | 7787 | } |
|
| 7697 | - | if prong.guard <> nil and branch.len > bindingsStart { |
|
| 7698 | - | throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 7788 | + | if prong.guard <> nil { |
|
| 7789 | + | for i in bindingsStart..branch.len { |
|
| 7790 | + | let sym = branch.symbols[i] else panic "checkLinearMatch: missing symbol"; |
|
| 7791 | + | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7792 | + | else panic "checkLinearMatch: expected value symbol"; |
|
| 7793 | + | if isLinear(ty) { |
|
| 7794 | + | throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 7795 | + | } |
|
| 7796 | + | } |
|
| 7699 | 7797 | } |
|
| 7700 | 7798 | if let guard = prong.guard { |
|
| 7701 | 7799 | try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume); |
|
| 7702 | 7800 | } |
|
| 7703 | 7801 | try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard); |
| 7720 | 7818 | env: *mut LinearEnv, |
|
| 7721 | 7819 | node: *ast::Node, |
|
| 7722 | 7820 | call: ast::Call, |
|
| 7723 | 7821 | ) throws (ResolveError) { |
|
| 7724 | 7822 | try checkLinearNode(checker, env, call.callee, LinearUse::Observe); |
|
| 7725 | - | let calleeTy = typeFor(checker.resolver, call.callee) else { |
|
| 7726 | - | throw emitError(checker.resolver, call.callee, ErrorKind::Internal); |
|
| 7727 | - | }; |
|
| 7728 | - | let case Type::Fn(info) = calleeTy else { |
|
| 7823 | + | let mut fnInfo: ?*FnType = nil; |
|
| 7824 | + | match checker.resolver.nodeData.entries[node.id].extra { |
|
| 7825 | + | case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => |
|
| 7826 | + | set fnInfo = traitInfo.methods[methodIndex].fnType, |
|
| 7827 | + | case NodeExtra::MethodCall { method } => set fnInfo = method.fnType, |
|
| 7828 | + | else => { |
|
| 7829 | + | if let calleeTy = typeFor(checker.resolver, call.callee) { |
|
| 7830 | + | if let case Type::Fn(info) = calleeTy { |
|
| 7831 | + | set fnInfo = info; |
|
| 7832 | + | } |
|
| 7833 | + | } |
|
| 7834 | + | } |
|
| 7835 | + | } |
|
| 7836 | + | let info = fnInfo else { |
|
| 7729 | 7837 | for arg in call.args { |
|
| 7730 | 7838 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 7731 | 7839 | } |
|
| 7732 | 7840 | return; |
|
| 7733 | 7841 | }; |
| 7775 | 7883 | } |
|
| 7776 | 7884 | ||
| 7777 | 7885 | for arg, i in call.args { |
|
| 7778 | 7886 | let expected = *info.paramTypes[i]; |
|
| 7779 | 7887 | let root = linearRootSymbol(checker.resolver, arg); |
|
| 7780 | - | let mut argExclusive = isLinear(expected); |
|
| 7888 | + | let mut argExclusive = isMoveOnly(expected); |
|
| 7781 | 7889 | if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7782 | 7890 | set argExclusive = mutable; |
|
| 7783 | 7891 | } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7784 | 7892 | set argExclusive = mutable; |
|
| 7785 | 7893 | } else if let case Type::TraitObject { |
| 7875 | 7983 | } |
|
| 7876 | 7984 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
|
| 7877 | 7985 | } |
|
| 7878 | 7986 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
|
| 7879 | 7987 | case ast::NodeValue::Let(binding) => { |
|
| 7988 | + | let mut isUndefined = false; |
|
| 7880 | 7989 | if let case ast::NodeValue::Undef = binding.value.value { |
|
| 7990 | + | set isUndefined = true; |
|
| 7991 | + | } |
|
| 7992 | + | if isUndefined { |
|
| 7881 | 7993 | if let bindingTy = typeFor(checker.resolver, binding.ident); |
|
| 7882 | 7994 | isLinear(bindingTy) |
|
| 7883 | 7995 | { |
|
| 7884 | 7996 | throw emitError( |
|
| 7885 | 7997 | checker.resolver, |
| 7888 | 8000 | ); |
|
| 7889 | 8001 | } |
|
| 7890 | 8002 | } |
|
| 7891 | 8003 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
|
| 7892 | 8004 | try addLinearBinding(checker, env, node); |
|
| 8005 | + | if isUndefined { |
|
| 8006 | + | markLinearBindingUnavailable(checker.resolver, env, node); |
|
| 8007 | + | } |
|
| 7893 | 8008 | } |
|
| 7894 | 8009 | case ast::NodeValue::Assign(assign) => { |
|
| 7895 | 8010 | let mut target: ?u32 = nil; |
|
| 8011 | + | let mut targetLinear = false; |
|
| 7896 | 8012 | if let leftTy = typeFor(checker.resolver, assign.left) { |
|
| 7897 | - | if isLinear(leftTy) { |
|
| 8013 | + | if isMoveOnly(leftTy) { |
|
| 8014 | + | set targetLinear = isLinear(leftTy); |
|
| 7898 | 8015 | if let case ast::NodeValue::Ident(_) = assign.left.value { |
|
| 7899 | 8016 | if let sym = symbolFor(checker.resolver, assign.left) { |
|
| 7900 | 8017 | set target = findLinearBinding(env, sym); |
|
| 7901 | 8018 | } |
|
| 7902 | 8019 | } |
| 7910 | 8027 | } |
|
| 7911 | 8028 | } |
|
| 7912 | 8029 | try checkLinearNode(checker, env, assign.left, LinearUse::Place); |
|
| 7913 | 8030 | try checkLinearNode(checker, env, assign.right, LinearUse::Consume); |
|
| 7914 | 8031 | if let index = target { |
|
| 7915 | - | if linearBindingAvailable(env, index) { |
|
| 8032 | + | if targetLinear and linearBindingAvailable(env, index) { |
|
| 7916 | 8033 | throw emitError( |
|
| 7917 | 8034 | checker.resolver, |
|
| 7918 | 8035 | assign.left, |
|
| 7919 | 8036 | ErrorKind::LinearOverwrite, |
|
| 7920 | 8037 | ); |
| 7926 | 8043 | case ast::NodeValue::AddressOf(addr) => { |
|
| 7927 | 8044 | try checkLinearNode(checker, env, addr.target, LinearUse::Borrow); |
|
| 7928 | 8045 | } |
|
| 7929 | 8046 | case ast::NodeValue::Deref(target) => { |
|
| 7930 | 8047 | if let resultTy = typeFor(checker.resolver, node) { |
|
| 7931 | - | if isLinear(resultTy) and usage == LinearUse::Consume { |
|
| 8048 | + | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 7932 | 8049 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 7933 | 8050 | } |
|
| 7934 | 8051 | } |
|
| 7935 | 8052 | try checkLinearNode(checker, env, target, LinearUse::Observe); |
|
| 7936 | 8053 | } |
|
| 7937 | 8054 | case ast::NodeValue::FieldAccess(access) => { |
|
| 7938 | 8055 | if let resultTy = typeFor(checker.resolver, node) { |
|
| 7939 | - | if isLinear(resultTy) and usage == LinearUse::Consume { |
|
| 8056 | + | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 7940 | 8057 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 7941 | 8058 | } |
|
| 7942 | 8059 | } |
|
| 7943 | 8060 | try checkLinearNode(checker, env, access.parent, LinearUse::Observe); |
|
| 7944 | 8061 | } |
|
| 7945 | 8062 | case ast::NodeValue::ScopeAccess(_) => {} |
|
| 7946 | 8063 | case ast::NodeValue::Subscript { container, index } => { |
|
| 7947 | 8064 | if let resultTy = typeFor(checker.resolver, node) { |
|
| 7948 | - | if isLinear(resultTy) and usage == LinearUse::Consume { |
|
| 8065 | + | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 7949 | 8066 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 7950 | 8067 | } |
|
| 7951 | 8068 | } |
|
| 7952 | 8069 | try checkLinearNode(checker, env, container, LinearUse::Observe); |
|
| 7953 | 8070 | try checkLinearNode(checker, env, index, LinearUse::Consume); |
| 7964 | 8081 | try checkLinearNode(checker, env, item, LinearUse::Consume); |
|
| 7965 | 8082 | } |
|
| 7966 | 8083 | } |
|
| 7967 | 8084 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 7968 | 8085 | if let itemTy = typeFor(checker.resolver, repeat.item) { |
|
| 7969 | - | if isLinear(itemTy) { |
|
| 8086 | + | if not isCopy(itemTy) { |
|
| 7970 | 8087 | throw emitError( |
|
| 7971 | 8088 | checker.resolver, |
|
| 7972 | 8089 | repeat.item, |
|
| 7973 | 8090 | ErrorKind::LinearDiscard, |
|
| 7974 | 8091 | ); |
lib/std/lang/resolver/printer.rad
+9 -0
| 494 | 494 | case super::ErrorKind::TraitMethodOverflow(m) => |
|
| 495 | 495 | printMismatch("too many trait methods", "maximum", m), |
|
| 496 | 496 | case super::ErrorKind::MissingSupertraitInstance(name) => { |
|
| 497 | 497 | printQuoted("missing instance for supertrait '", name); |
|
| 498 | 498 | } |
|
| 499 | + | case super::ErrorKind::AffineUseAfterMove(name) => { |
|
| 500 | + | printQuoted("affine value used after move: '", name); |
|
| 501 | + | } |
|
| 499 | 502 | case super::ErrorKind::LinearUseAfterConsume(name) => { |
|
| 500 | 503 | printQuoted("linear value used after consumption: '", name); |
|
| 501 | 504 | } |
|
| 502 | 505 | case super::ErrorKind::LinearNotConsumed(name) => { |
|
| 503 | 506 | printQuoted("linear value is not consumed: '", name); |
| 518 | 521 | io::print("assignment would overwrite a linear value"); |
|
| 519 | 522 | } |
|
| 520 | 523 | case super::ErrorKind::LinearUndefined => { |
|
| 521 | 524 | io::print("linear values cannot be undefined"); |
|
| 522 | 525 | } |
|
| 526 | + | case super::ErrorKind::CopyContainsNonCopy => { |
|
| 527 | + | io::print("`Copy` composite contains a non-copy value"); |
|
| 528 | + | } |
|
| 529 | + | case super::ErrorKind::ConflictingOwnershipMarkers => { |
|
| 530 | + | io::print("a composite cannot be both `Copy` and `Linear`"); |
|
| 531 | + | } |
|
| 523 | 532 | case super::ErrorKind::InvalidRefPosition => { |
|
| 524 | 533 | io::print("reference type is only allowed as a function parameter"); |
|
| 525 | 534 | } |
|
| 526 | 535 | case super::ErrorKind::RefBinding => { |
|
| 527 | 536 | io::print("references cannot be bound to locals"); |
lib/std/lang/resolver/tests.rad
+57 -1
| 51 | 51 | "Second", "Opt", "x", |
|
| 52 | 52 | "value", "idx" |
|
| 53 | 53 | ]; |
|
| 54 | 54 | ||
| 55 | 55 | /// Resolver result with AST, used by test helpers. |
|
| 56 | - | record TestResult { |
|
| 56 | + | record TestResult: Copy { |
|
| 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. |
| 279 | 279 | if let case super::ErrorKind::MissingSupertraitInstance(actualName) = *actual { |
|
| 280 | 280 | return mem::eq(actualName, expectedName); |
|
| 281 | 281 | } |
|
| 282 | 282 | return false; |
|
| 283 | 283 | } |
|
| 284 | + | if let case super::ErrorKind::AffineUseAfterMove(expectedName) = expected { |
|
| 285 | + | if let case super::ErrorKind::AffineUseAfterMove(actualName) = *actual { |
|
| 286 | + | return mem::eq(actualName, expectedName); |
|
| 287 | + | } |
|
| 288 | + | return false; |
|
| 289 | + | } |
|
| 284 | 290 | if let case super::ErrorKind::LinearUseAfterConsume(expectedName) = expected { |
|
| 285 | 291 | if let case super::ErrorKind::LinearUseAfterConsume(actualName) = *actual { |
|
| 286 | 292 | return mem::eq(actualName, expectedName); |
|
| 287 | 293 | } |
|
| 288 | 294 | return false; |
| 5238 | 5244 | let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }"; |
|
| 5239 | 5245 | let result = try resolveProgramStr(&mut a, program); |
|
| 5240 | 5246 | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 5241 | 5247 | } |
|
| 5242 | 5248 | ||
| 5249 | + | /// Unmarked composite values may be discarded. |
|
| 5250 | + | @test fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) { |
|
| 5251 | + | let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }"; |
|
| 5252 | + | try expectAnalyzeOk(program); |
|
| 5253 | + | } |
|
| 5254 | + | ||
| 5255 | + | /// A by-value use moves an unmarked composite value. |
|
| 5256 | + | @test fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) { |
|
| 5257 | + | let mut a = testResolver(); |
|
| 5258 | + | let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 5259 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5260 | + | try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value")); |
|
| 5261 | + | } |
|
| 5262 | + | ||
| 5263 | + | /// Affine values may move on only one branch when not used later. |
|
| 5264 | + | @test fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) { |
|
| 5265 | + | let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }"; |
|
| 5266 | + | try expectAnalyzeOk(program); |
|
| 5267 | + | } |
|
| 5268 | + | ||
| 5269 | + | /// A `Copy` composite remains available after a by-value use. |
|
| 5270 | + | @test fn testCopyCompositeMayBeReused() throws (testing::TestError) { |
|
| 5271 | + | let program = "record Value: Copy { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 5272 | + | try expectAnalyzeOk(program); |
|
| 5273 | + | } |
|
| 5274 | + | ||
| 5275 | + | /// A `Copy` composite may contain only copy values. |
|
| 5276 | + | @test fn testCopyCompositeRejectsAffineField() throws (testing::TestError) { |
|
| 5277 | + | let mut a = testResolver(); |
|
| 5278 | + | let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }"; |
|
| 5279 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5280 | + | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 5281 | + | } |
|
| 5282 | + | ||
| 5283 | + | /// A composite cannot carry conflicting ownership markers. |
|
| 5284 | + | @test fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) { |
|
| 5285 | + | let mut a = testResolver(); |
|
| 5286 | + | let program = "record Value: Copy + Linear { number: u32 }"; |
|
| 5287 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5288 | + | try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers); |
|
| 5289 | + | } |
|
| 5290 | + | ||
| 5291 | + | /// Linear composites still require one consuming use. |
|
| 5292 | + | @test fn testLinearCompositeMustBeConsumed() throws (testing::TestError) { |
|
| 5293 | + | let mut a = testResolver(); |
|
| 5294 | + | let program = "record Token: Linear { number: u32 } fn run() { let token = Token { number: 1 }; }"; |
|
| 5295 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5296 | + | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
|
| 5297 | + | } |
|
| 5298 | + | ||
| 5243 | 5299 | /// The compiler-known marker cannot be derived more than once. |
|
| 5244 | 5300 | @test fn testDuplicateLinearMarkerRejected() throws (testing::TestError) { |
|
| 5245 | 5301 | let mut a = testResolver(); |
|
| 5246 | 5302 | let program = "record Token: Linear + Linear { value: u32 }"; |
|
| 5247 | 5303 | let result = try resolveProgramStr(&mut a, program); |
lib/std/lang/scanner.rad
+6 -6
| 10 | 10 | ||
| 11 | 11 | /// Token kinds representing all lexical elements in Radiance. |
|
| 12 | 12 | /// |
|
| 13 | 13 | /// This enum covers operators, keywords, literals, and structural |
|
| 14 | 14 | /// elements used by the parser to build the AST. |
|
| 15 | - | export union TokenKind { |
|
| 15 | + | export union TokenKind: Copy { |
|
| 16 | 16 | /// Special end of file token generated when the input is exhausted. |
|
| 17 | 17 | Eof, |
|
| 18 | 18 | /// Special invalid token. |
|
| 19 | 19 | Invalid, |
|
| 20 | 20 |
| 107 | 107 | I8, I16, I32, I64, U8, U16, U32, U64, |
|
| 108 | 108 | Opaque, Fn, Bool, Union, Record, As, Unsafe |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// A reserved keyword. |
|
| 112 | - | record Keyword { |
|
| 112 | + | record Keyword: Copy { |
|
| 113 | 113 | /// Keyword string. |
|
| 114 | 114 | name: *[u8], |
|
| 115 | 115 | /// Corresponding token. |
|
| 116 | 116 | tok: TokenKind, |
|
| 117 | 117 | } |
| 171 | 171 | { name: "use", tok: TokenKind::Use }, |
|
| 172 | 172 | { name: "while", tok: TokenKind::While }, |
|
| 173 | 173 | ]; |
|
| 174 | 174 | ||
| 175 | 175 | /// Describes where source code originated from. |
|
| 176 | - | export union SourceLoc { |
|
| 176 | + | export union SourceLoc: Copy { |
|
| 177 | 177 | /// Source loaded from a file at the given path. |
|
| 178 | 178 | File(*[u8]), |
|
| 179 | 179 | /// Source provided as an inline string (no file path). |
|
| 180 | 180 | String, |
|
| 181 | 181 | } |
|
| 182 | 182 | ||
| 183 | 183 | /// Lexical scanner state for tokenizing Radiance source code. |
|
| 184 | 184 | /// |
|
| 185 | 185 | /// Maintains position information and source buffer reference. |
|
| 186 | - | export record Scanner { |
|
| 186 | + | export record Scanner: Copy { |
|
| 187 | 187 | /// Origin of the source being scanned. |
|
| 188 | 188 | sourceLoc: SourceLoc, |
|
| 189 | 189 | /// Source buffer. |
|
| 190 | 190 | source: *[u8], |
|
| 191 | 191 | /// Offset of current token into buffer. |
| 198 | 198 | ||
| 199 | 199 | /// Individual token with kind, source text, and position. |
|
| 200 | 200 | /// |
|
| 201 | 201 | /// Represents a single lexical element extracted from source, |
|
| 202 | 202 | /// including its original text and byte offset for error reporting. |
|
| 203 | - | export record Token { |
|
| 203 | + | export record Token: Copy { |
|
| 204 | 204 | /// Token kind. |
|
| 205 | 205 | kind: TokenKind, |
|
| 206 | 206 | /// Token source string. |
|
| 207 | 207 | source: *[u8], |
|
| 208 | 208 | /// Byte offset of `source` in input buffer. |
| 210 | 210 | } |
|
| 211 | 211 | ||
| 212 | 212 | /// Source code location with line/column information. |
|
| 213 | 213 | /// |
|
| 214 | 214 | /// Used for error reporting and debugging. |
|
| 215 | - | export record Location { |
|
| 215 | + | export record Location: Copy { |
|
| 216 | 216 | /// Origin of the source. |
|
| 217 | 217 | source: SourceLoc, |
|
| 218 | 218 | /// Line number. |
|
| 219 | 219 | line: u16, |
|
| 220 | 220 | /// Column number. |
lib/std/lang/sexpr.rad
+2 -2
| 5 | 5 | ||
| 6 | 6 | use std::io; |
|
| 7 | 7 | use std::lang::alloc; |
|
| 8 | 8 | ||
| 9 | 9 | /// Output target for S-expression printing. |
|
| 10 | - | export union Output { |
|
| 10 | + | export union Output: Copy { |
|
| 11 | 11 | /// Print to stdout. |
|
| 12 | 12 | Stdout, |
|
| 13 | 13 | /// Write to a buffer, tracking position. |
|
| 14 | 14 | Buffer { buf: *mut [u8], pos: *mut u32 }, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// An S-expression element. |
|
| 18 | - | export union Expr { |
|
| 18 | + | export union Expr: Copy { |
|
| 19 | 19 | /// An empty expression. |
|
| 20 | 20 | Null, |
|
| 21 | 21 | /// A symbol/identifier. |
|
| 22 | 22 | Sym(*[u8]), |
|
| 23 | 23 | /// A quoted string literal. |
lib/std/lang/strings.rad
+2 -2
| 14 | 14 | ||
| 15 | 15 | /// String interning pool using open-addressed hash table. |
|
| 16 | 16 | /// |
|
| 17 | 17 | /// Each unique string content is stored only once, allowing pointer equality |
|
| 18 | 18 | /// to be used instead of content comparison for symbol lookups and module names. |
|
| 19 | - | export record Pool { |
|
| 19 | + | export record Pool: Copy { |
|
| 20 | 20 | /// Hash table slots. |
|
| 21 | 21 | table: [*[u8]; TABLE_SIZE], |
|
| 22 | 22 | /// Number of strings currently in the pool. |
|
| 23 | 23 | count: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Lookup result. |
|
| 27 | - | union Lookup { |
|
| 27 | + | union Lookup: Copy { |
|
| 28 | 28 | /// Found. |
|
| 29 | 29 | Found(*[u8]), |
|
| 30 | 30 | /// Not found, contains empty slot index. |
|
| 31 | 31 | Empty(u32), |
|
| 32 | 32 | } |
lib/std/lang/types.rad
+1 -1
| 1 | 1 | //! Shared Radiance language types. |
|
| 2 | 2 | ||
| 3 | 3 | /// Ownership and safety class for pointer-like types. |
|
| 4 | - | export union PointerClass { |
|
| 4 | + | export union PointerClass: Copy { |
|
| 5 | 5 | /// Owned pointer, eg. `*T`. |
|
| 6 | 6 | Owned, |
|
| 7 | 7 | /// Reference, borrowed pointer, eg. `&T`. |
|
| 8 | 8 | Ref, |
|
| 9 | 9 | /// Unsafe, raw pointer, eg. `*unsafe T`. |
lib/std/mem.rad
+1 -1
| 1 | 1 | /// Memory error. |
|
| 2 | - | union MemoryError { |
|
| 2 | + | union MemoryError: Copy { |
|
| 3 | 3 | /// Buffer is too small. |
|
| 4 | 4 | BufferTooSmall, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Copy bytes between two slices. Returns the number of bytes copied. |
lib/std/sys.rad
+1 -1
| 1 | 1 | //! System utilities. |
|
| 2 | 2 | export mod unix; |
|
| 3 | 3 | ||
| 4 | 4 | /// Process environment passed to the program entry point. |
|
| 5 | - | export record Env { |
|
| 5 | + | export record Env: Copy { |
|
| 6 | 6 | /// Command-line arguments. |
|
| 7 | 7 | args: *[*[u8]], |
|
| 8 | 8 | } |
lib/std/sys/unix.rad
+1 -1
| 1 | 1 | //! Unix-specific system calls and utilities. |
|
| 2 | 2 | use std::intrinsics; |
|
| 3 | 3 | ||
| 4 | 4 | /// File access modes. |
|
| 5 | - | export record OpenFlags(i64); |
|
| 5 | + | export record OpenFlags: Copy(i64); |
|
| 6 | 6 | ||
| 7 | 7 | /// Open file for reading only. |
|
| 8 | 8 | export constant O_RDONLY: OpenFlags = OpenFlags(0); |
|
| 9 | 9 | ||
| 10 | 10 | /// Open file for writing only. |
lib/std/testing.rad
+3 -3
| 2 | 2 | ||
| 3 | 3 | use std::mem; |
|
| 4 | 4 | use std::io; |
|
| 5 | 5 | ||
| 6 | 6 | /// Internal state for tracking test pass/fail counts. |
|
| 7 | - | record Ctx { |
|
| 7 | + | record Ctx: Copy { |
|
| 8 | 8 | passed: u32, |
|
| 9 | 9 | failed: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Error thrown when a test assertion fails. |
|
| 13 | - | export union TestError { |
|
| 13 | + | export union TestError: Copy { |
|
| 14 | 14 | Failed, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Descriptor for a single test case, holding its module path, name, and entry point. |
|
| 18 | - | export record TestInfo { |
|
| 18 | + | export record TestInfo: Copy { |
|
| 19 | 19 | module: *[u8], |
|
| 20 | 20 | name: *[u8], |
|
| 21 | 21 | func: fn() throws (TestError), |
|
| 22 | 22 | } |
|
| 23 | 23 |
lib/std/tests.rad
+1 -1
| 6 | 6 | use std::testing; |
|
| 7 | 7 | use std::sys::unix; |
|
| 8 | 8 | ||
| 9 | 9 | // Data types ////////////////////////////////////////////////////////////////// |
|
| 10 | 10 | ||
| 11 | - | record Point { |
|
| 11 | + | record Point: Copy { |
|
| 12 | 12 | x: i32, |
|
| 13 | 13 | y: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | // fmt ///////////////////////////////////////////////////////////////////////// |
lib/std/vec.rad
+1 -1
| 5 | 5 | //! to the element type's requirements. |
|
| 6 | 6 | ||
| 7 | 7 | /// Raw vector metadata structure. |
|
| 8 | 8 | /// |
|
| 9 | 9 | /// Does not own storage, points to user-provided arena. |
|
| 10 | - | export record RawVec { |
|
| 10 | + | export record RawVec: Copy { |
|
| 11 | 11 | /// Pointer to user-provided byte arena. |
|
| 12 | 12 | data: *mut [u8], |
|
| 13 | 13 | /// Current number of elements stored. |
|
| 14 | 14 | len: u32, |
|
| 15 | 15 | /// Size of each element in bytes (stride between elements). |
seed/radiance.rv64
+0 -0
Binary file changed.
seed/radiance.rv64.git
+1 -1
| 1 | - | d024ed30c27cc671d9371988d0155b3e08ff004582cf59f1b7eac8ffdf3e3467 |
|
| 1 | + | 1e2fdea952350036a4172f567442e608555094b9e18fa69f1fb615453d5352b6 |
test/tests/abi.sizes.rad
+5 -5
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test size-based ABI: small aggregates (<= 8 bytes) are passed/returned |
|
| 3 | 3 | //! by value in registers, while larger aggregates use hidden pointers. |
|
| 4 | 4 | ||
| 5 | - | record Byte { |
|
| 5 | + | record Byte: Copy { |
|
| 6 | 6 | x: u8, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Short { |
|
| 9 | + | record Short: Copy { |
|
| 10 | 10 | x: u16, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | record Word { |
|
| 13 | + | record Word: Copy { |
|
| 14 | 14 | x: u32, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | record Pair { |
|
| 17 | + | record Pair: Copy { |
|
| 18 | 18 | a: u32, |
|
| 19 | 19 | b: u32, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | record Triple { |
|
| 22 | + | record Triple: Copy { |
|
| 23 | 23 | x: u32, |
|
| 24 | 24 | y: u32, |
|
| 25 | 25 | z: u32, |
|
| 26 | 26 | } |
|
| 27 | 27 |
test/tests/aggregate.return.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that returning aggregate types via hidden return buffer does not |
|
| 3 | 3 | //! corrupt the caller's stack. Modifying a returned aggregate must not affect |
|
| 4 | 4 | //! other locals or previously returned values. |
|
| 5 | 5 | ||
| 6 | - | record Pair { |
|
| 6 | + | record Pair: Copy { |
|
| 7 | 7 | a: u32, |
|
| 8 | 8 | b: u32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | record Triple { |
|
| 11 | + | record Triple: Copy { |
|
| 12 | 12 | x: u32, |
|
| 13 | 13 | y: u32, |
|
| 14 | 14 | z: u32, |
|
| 15 | 15 | } |
|
| 16 | 16 |
test/tests/array.aggregate.stride.rad
+1 -1
| 1 | 1 | /// Tests for correct stride calculation with aggregate array elements. |
|
| 2 | 2 | /// This is important because aggregate types (records, arrays) are larger |
|
| 3 | 3 | /// than a single word and require correct stride (not just 4 bytes). |
|
| 4 | 4 | ||
| 5 | - | record Point { x: i32, y: i32 } |
|
| 5 | + | record Point: Copy { x: i32, y: i32 } |
|
| 6 | 6 | ||
| 7 | 7 | /// Array of records (8 bytes per element). |
|
| 8 | 8 | fn arrayOfRecords(arr: [Point; 3], idx: u32) -> i32 { |
|
| 9 | 9 | return arr[idx].x; |
|
| 10 | 10 | } |
test/tests/array.record.elements.rad
+1 -1
| 1 | 1 | //! returns: 50 |
|
| 2 | 2 | //! Test arrays with record elements. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn calculateDistance(p1: Point, p2: Point) -> i32 { |
test/tests/assign.mutable.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test suite for stack memory allocation and aliasing behavior. |
|
| 3 | 3 | //! Tests variable aliasing, mutations, and stack frame management. |
|
| 4 | 4 | ||
| 5 | - | record Person { |
|
| 5 | + | record Person: Copy { |
|
| 6 | 6 | age: i32, |
|
| 7 | 7 | score: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Container { |
|
| 10 | + | record Container: Copy { |
|
| 11 | 11 | values: [i32; 3], |
|
| 12 | 12 | count: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Basic variable aliasing and mutation. |
test/tests/bool.comparison.nested.gen.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested record equality. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Circle { |
|
| 9 | + | record Circle: Copy { |
|
| 10 | 10 | center: Point, |
|
| 11 | 11 | radius: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | fn testNestedEqual() -> bool { |
test/tests/bool.comparison.record.gen.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Rect { |
|
| 8 | + | record Rect: Copy { |
|
| 9 | 9 | width: u32, |
|
| 10 | 10 | height: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn testPointEqual() -> bool { |
test/tests/bool.comparison.record.rad
+2 -2
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | ||
| 3 | 3 | // A packed record. 8 bytes. |
|
| 4 | - | record Packed { |
|
| 4 | + | record Packed: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32 |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // A record with padding. 17 bytes. |
|
| 10 | - | record Padded { |
|
| 10 | + | record Padded: Copy { |
|
| 11 | 11 | a: u8, |
|
| 12 | 12 | b: u32, |
|
| 13 | 13 | c: u16, |
|
| 14 | 14 | d: u32, |
|
| 15 | 15 | e: u8, |
test/tests/bool.comparison.slice.record.gen.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Line { |
|
| 8 | + | record Line: Copy { |
|
| 9 | 9 | points: *[Point], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 13 | 13 | if a.len <> b.len { |
test/tests/bool.comparison.slice.union.gen.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Size { |
|
| 8 | + | record Size: Copy { |
|
| 9 | 9 | width: u32, |
|
| 10 | 10 | height: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | union Shape { |
|
| 13 | + | union Shape: Copy { |
|
| 14 | 14 | points(*[Point]), |
|
| 15 | 15 | sizes(*[Size]), |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
test/tests/bool.comparison.union.ctor.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test comparing a void union variable against a constructor literal. |
|
| 3 | 3 | ||
| 4 | - | union Op { |
|
| 4 | + | union Op: Copy { |
|
| 5 | 5 | Add, |
|
| 6 | 6 | Sub, |
|
| 7 | 7 | Mul, |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/bool.comparison.union.gen.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union Status { |
|
| 3 | + | union Status: Copy { |
|
| 4 | 4 | pending, |
|
| 5 | 5 | running(u32), |
|
| 6 | 6 | completed(bool), |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/bool.comparison.union.record.gen.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Size { |
|
| 8 | + | record Size: Copy { |
|
| 9 | 9 | width: u32, |
|
| 10 | 10 | height: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | union Shape { |
|
| 13 | + | union Shape: Copy { |
|
| 14 | 14 | circle(u32), |
|
| 15 | 15 | point(Point), |
|
| 16 | 16 | rect(Size), |
|
| 17 | 17 | } |
|
| 18 | 18 |
test/tests/bool.comparison.union.simple.gen.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test equality on simple enum without payloads. |
|
| 3 | - | union Color { |
|
| 3 | + | union Color: Copy { |
|
| 4 | 4 | red, |
|
| 5 | 5 | green, |
|
| 6 | 6 | blue, |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/builtin.alignof.rad
+1 -1
| 1 | 1 | /// A simple record with mixed field sizes for alignment testing. |
|
| 2 | - | record Mixed { |
|
| 2 | + | record Mixed: Copy { |
|
| 3 | 3 | a: u8, |
|
| 4 | 4 | b: u32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Returns the alignment of a u8. |
test/tests/builtin.size.align.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | - | record Foo { |
|
| 2 | + | record Foo: Copy { |
|
| 3 | 3 | x: u8, |
|
| 4 | 4 | y: u32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | @default fn main() -> u8 { |
test/tests/builtin.sizeof.rad
+1 -1
| 1 | 1 | /// A simple record with mixed field sizes for alignment testing. |
|
| 2 | - | record Mixed { |
|
| 2 | + | record Mixed: Copy { |
|
| 3 | 3 | a: u8, |
|
| 4 | 4 | b: u32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Returns the size of a u8. |
test/tests/call.aggregate.arg.snapshot.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Aggregate call arguments must capture their value when evaluated, before |
|
| 3 | 3 | //! later arguments run and mutate the source object. |
|
| 4 | 4 | ||
| 5 | - | record Pair { |
|
| 5 | + | record Pair: Copy { |
|
| 6 | 6 | x: i32, |
|
| 7 | 7 | y: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | fn mutate(pair: *mut Pair) -> i32 { |
test/tests/compound.assign.field.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | /// Test compound assignment on record fields. |
|
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | @default fn main() -> i32 { |
test/tests/cond.expr.aggregate.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test conditional expression with aggregate types (tagged unions, optionals). |
|
| 3 | 3 | ||
| 4 | - | union Val { |
|
| 4 | + | union Val: Copy { |
|
| 5 | 5 | Reg(u32), |
|
| 6 | 6 | Imm(i64), |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn pickVal(flag: bool, reg: u32) -> Val { |
test/tests/cond.expr.rad
+1 -1
| 7 | 7 | ||
| 8 | 8 | fn pick(cond: bool, a: i32, b: i32) -> i32 { |
|
| 9 | 9 | return a if cond else b; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | union Color { Red, Green, Blue } |
|
| 12 | + | union Color: Copy { Red, Green, Blue } |
|
| 13 | 13 | ||
| 14 | 14 | fn colorPick(isRed: bool) -> Color { |
|
| 15 | 15 | return Color::Red if isRed else Color::Blue; |
|
| 16 | 16 | } |
|
| 17 | 17 |
test/tests/cond.if.case.rad
+1 -1
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | //! Test if-let-case with union variants. |
|
| 3 | 3 | ||
| 4 | - | union Response { |
|
| 4 | + | union Response: Copy { |
|
| 5 | 5 | success(i32), |
|
| 6 | 6 | failure, |
|
| 7 | 7 | offline, |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/cond.match.guard.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union OptionNum { |
|
| 3 | + | union OptionNum: Copy { |
|
| 4 | 4 | Some(i32), |
|
| 5 | 5 | Other(i32), |
|
| 6 | 6 | None, |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/cond.match.guard.regalloc.rad
+2 -2
| 5 | 5 | //! `case Kind::Name(name) if name.len > 0` creates a block ordering where |
|
| 6 | 6 | //! the case body has a lower index than its predecessor (the guard block). |
|
| 7 | 7 | //! Without RPO-based register allocation, the name payload pointer can be |
|
| 8 | 8 | //! overwritten by the bounds check in the case body. |
|
| 9 | 9 | ||
| 10 | - | union Kind { |
|
| 10 | + | union Kind: Copy { |
|
| 11 | 11 | Num(i32), |
|
| 12 | 12 | Name(*[u8]), |
|
| 13 | 13 | Pair { a: i32, b: i32 }, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | record Node { |
|
| 16 | + | record Node: Copy { |
|
| 17 | 17 | id: u32, |
|
| 18 | 18 | span1: u32, |
|
| 19 | 19 | span2: u32, |
|
| 20 | 20 | kind: Kind, |
|
| 21 | 21 | } |
test/tests/const.array.record.ident.rad
+1 -1
| 1 | 1 | /// Test constant array containing references to other record constants. |
|
| 2 | 2 | /// This tests that identifiers referencing record constants work in constant arrays. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | constant P1: Point = Point { x: 1, y: 2 }; |
test/tests/const.array.repeat.record.rad
+1 -1
| 1 | 1 | /// Test constant array repeat with record elements. |
|
| 2 | - | record Point { x: i32, y: i32 } |
|
| 2 | + | record Point: Copy { x: i32, y: i32 } |
|
| 3 | 3 | ||
| 4 | 4 | constant POINTS: [Point; 3] = [Point { x: 1, y: 2 }; 3]; |
|
| 5 | 5 | ||
| 6 | 6 | fn getSum() -> i32 { |
|
| 7 | 7 | return POINTS[0].x + POINTS[1].y + POINTS[2].x; |
test/tests/const.record.array.rad
+1 -1
| 1 | 1 | //! returns: 129 |
|
| 2 | 2 | //! Test array of record constants. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // A constant array of structs |
test/tests/const.record.array.simple.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test array of record constants. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // Define an array of record constants |
test/tests/const.record.ctor.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Constant unlabeled record constructor. |
|
| 3 | - | record Pair(i32, i32); |
|
| 3 | + | record Pair: Copy(i32, i32); |
|
| 4 | 4 | ||
| 5 | 5 | constant P: Pair = Pair(40, 2); |
|
| 6 | 6 | ||
| 7 | 7 | @default fn main() -> i32 { |
|
| 8 | 8 | assert P == Pair(40, 2); |
test/tests/const.record.fn.rad
+1 -1
| 1 | 1 | //! returns: 7 |
|
| 2 | 2 | //! Test record constants passed to functions. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | constant ORIGIN: Point = Point { x: 3, y: 4 }; |
test/tests/const.record.mutcopy.rad
+1 -1
| 1 | 1 | /// Test that accessing an aggregate constant produces a mutable copy |
|
| 2 | 2 | /// (via Reserve + Blit) so that code can safely assign through the pointer. |
|
| 3 | 3 | ||
| 4 | - | record Reg { n: u8 } |
|
| 4 | + | record Reg: Copy { n: u8 } |
|
| 5 | 5 | ||
| 6 | 6 | constant SCRATCH1: Reg = { n: 30 }; |
|
| 7 | 7 | constant SCRATCH2: Reg = { n: 31 }; |
|
| 8 | 8 | ||
| 9 | 9 | /// Returns a scratch register, avoiding the one already used by `rs`. |
test/tests/const.record.nested.rad
+2 -2
| 1 | 1 | /// Test constant nested record lowering with padding. |
|
| 2 | - | record Inner { |
|
| 2 | + | record Inner: Copy { |
|
| 3 | 3 | a: u8, |
|
| 4 | 4 | b: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | record Outer { |
|
| 7 | + | record Outer: Copy { |
|
| 8 | 8 | x: u8, |
|
| 9 | 9 | inner: Inner, |
|
| 10 | 10 | y: u8, |
|
| 11 | 11 | } |
|
| 12 | 12 |
test/tests/const.record.packed.rad
+1 -1
| 1 | 1 | /// Test constant record with multiple sub-word fields. |
|
| 2 | - | record Packed { |
|
| 2 | + | record Packed: Copy { |
|
| 3 | 3 | a: u8, |
|
| 4 | 4 | b: u8, |
|
| 5 | 5 | c: u8, |
|
| 6 | 6 | } |
|
| 7 | 7 |
test/tests/const.record.padded.rad
+1 -1
| 1 | 1 | /// Test constant record with padding between fields. |
|
| 2 | - | record Padded { |
|
| 2 | + | record Padded: Copy { |
|
| 3 | 3 | a: u8, |
|
| 4 | 4 | b: i32, |
|
| 5 | 5 | c: u8, |
|
| 6 | 6 | } |
|
| 7 | 7 |
test/tests/const.record.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test record constants. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | constant ORIGIN: Point = Point { x: 3, y: 4 }; |
test/tests/const.record.union.rad
+2 -2
| 1 | 1 | /// Test constant array of records containing union variant fields. |
|
| 2 | 2 | /// This is similar to scanner.rad's KEYWORDS constant. |
|
| 3 | - | union TokenKind { |
|
| 3 | + | union TokenKind: Copy { |
|
| 4 | 4 | Fn, |
|
| 5 | 5 | Let, |
|
| 6 | 6 | If, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Keyword { |
|
| 9 | + | record Keyword: Copy { |
|
| 10 | 10 | name: *[u8], |
|
| 11 | 11 | tok: TokenKind, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | constant KEYWORDS: [Keyword; 3] = [ |
test/tests/const.union.payload.ctor.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Constant union payload constructor call. |
|
| 3 | - | union Value { |
|
| 3 | + | union Value: Copy { |
|
| 4 | 4 | Int(i32), |
|
| 5 | 5 | Bool(bool), |
|
| 6 | 6 | None, |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/const.union.payload.fn.array.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Constant array of records containing union variants with throwing function payloads. |
|
| 3 | 3 | ||
| 4 | - | union TestError { |
|
| 4 | + | union TestError: Copy { |
|
| 5 | 5 | Fail, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn add(a: i32, b: i32) -> i32 throws (TestError) { |
|
| 9 | 9 | return a + b; |
| 11 | 11 | ||
| 12 | 12 | fn sub(a: i32, b: i32) -> i32 throws (TestError) { |
|
| 13 | 13 | return a - b; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | union Handler { |
|
| 16 | + | union Handler: Copy { |
|
| 17 | 17 | Binary { op: fn(i32, i32) -> i32 throws (TestError) }, |
|
| 18 | 18 | None, |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | record Entry { |
|
| 21 | + | record Entry: Copy { |
|
| 22 | 22 | handler: Handler, |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | constant ENTRIES: [Entry; 3] = [ |
|
| 26 | 26 | Entry { handler: Handler::None }, |
test/tests/const.union.payload.record.array.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Constant array of records containing union variants with record payloads. |
|
| 3 | 3 | ||
| 4 | - | union InstrSpec { |
|
| 4 | + | union InstrSpec: Copy { |
|
| 5 | 5 | Empty, |
|
| 6 | 6 | Small { flag: bool }, |
|
| 7 | 7 | Imm { opcode: u32, funct3: u32, imm: i32 }, |
|
| 8 | 8 | Reg { opcode: u32, funct3: u32, funct7: u32 }, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | record Entry { |
|
| 11 | + | record Entry: Copy { |
|
| 12 | 12 | name: *[u8], |
|
| 13 | 13 | spec: InstrSpec, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | constant ENTRIES: [Entry; 4] = [ |
test/tests/const.union.record.literal.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Constant union record payload literal. |
|
| 3 | - | union Expr { |
|
| 3 | + | union Expr: Copy { |
|
| 4 | 4 | Nil, |
|
| 5 | 5 | Pair { first: i32, second: i32 }, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | constant E: Expr = Expr::Pair { first: 20, second: 22 }; |
test/tests/data.record.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test read-only data access for `record` types. |
|
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Mixed { |
|
| 8 | + | record Mixed: Copy { |
|
| 9 | 9 | a: u8, |
|
| 10 | 10 | b: u16, |
|
| 11 | 11 | c: u32, |
|
| 12 | 12 | d: bool, |
|
| 13 | 13 | } |
test/tests/data.union.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test read-only data access for `union` types. |
|
| 3 | - | union Color { |
|
| 3 | + | union Color: Copy { |
|
| 4 | 4 | Red, |
|
| 5 | 5 | Green, |
|
| 6 | 6 | Blue, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Value { |
|
| 9 | + | union Value: Copy { |
|
| 10 | 10 | None, |
|
| 11 | 11 | Small, |
|
| 12 | 12 | Large, |
|
| 13 | 13 | } |
|
| 14 | 14 |
test/tests/debug.tag.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union Huge { |
|
| 3 | + | union Huge: Copy { |
|
| 4 | 4 | Small(u32), |
|
| 5 | 5 | Large { |
|
| 6 | 6 | payload: [u32; 32], |
|
| 7 | 7 | flag: u32, |
|
| 8 | 8 | }, |
test/tests/edge.cases.2.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test scanner peek with optional return. |
|
| 3 | 3 | ||
| 4 | - | export record Scanner { |
|
| 4 | + | export record Scanner: Copy { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn peek(s: *Scanner) -> ?u8 { |
|
| 9 | 9 | if 0 + 0 >= s.source.len { |
test/tests/edge.cases.3.rad
+1 -1
| 1 | 1 | //! returns: 97 |
|
| 2 | 2 | //! Test scanner current character access. |
|
| 3 | 3 | ||
| 4 | - | export record Scanner { |
|
| 4 | + | export record Scanner: Copy { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | cursor: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn isEof(s: *Scanner) -> bool { |
test/tests/edge.cases.4.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union Signedness { |
|
| 3 | + | union Signedness: Copy { |
|
| 4 | 4 | Signed, |
|
| 5 | 5 | Unsigned, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | union TypeSig { |
|
| 8 | + | union TypeSig: Copy { |
|
| 9 | 9 | Integer { |
|
| 10 | 10 | width: u8, |
|
| 11 | 11 | sign: Signedness, |
|
| 12 | 12 | }, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | union NodeValue { |
|
| 15 | + | union NodeValue: Copy { |
|
| 16 | 16 | TypeSig(TypeSig), |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | record Node { |
|
| 19 | + | record Node: Copy { |
|
| 20 | 20 | value: NodeValue, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | fn node(nodes: *mut Node, count: *mut u32, value: NodeValue) -> *Node { |
|
| 24 | 24 | let index = *count; |
test/tests/edge.cases.5.rad
+9 -9
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union TestError { |
|
| 3 | + | union TestError: Copy { |
|
| 4 | 4 | Fail, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | record Derives { |
|
| 7 | + | record Derives: Copy { |
|
| 8 | 8 | len: i32, |
|
| 9 | 9 | list: [[u8; 8]; 2], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record StructField { |
|
| 12 | + | record StructField: Copy { |
|
| 13 | 13 | typeSize: i32, |
|
| 14 | 14 | isSigned: bool, |
|
| 15 | 15 | value: ?i32, |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | record FieldNode { |
|
| 18 | + | record FieldNode: Copy { |
|
| 19 | 19 | value: FieldValue, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | record FieldList { |
|
| 22 | + | record FieldList: Copy { |
|
| 23 | 23 | len: i32, |
|
| 24 | 24 | list: [FieldNode; 2], |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | record StructDecl { |
|
| 27 | + | record StructDecl: Copy { |
|
| 28 | 28 | derives: Derives, |
|
| 29 | 29 | fields: FieldList, |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | - | record Node { |
|
| 32 | + | record Node: Copy { |
|
| 33 | 33 | value: NodeValue, |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | union FieldValue { |
|
| 36 | + | union FieldValue: Copy { |
|
| 37 | 37 | StructField(StructField), |
|
| 38 | 38 | Other(i32), |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | - | union NodeValue { |
|
| 41 | + | union NodeValue: Copy { |
|
| 42 | 42 | StructDecl(StructDecl), |
|
| 43 | 43 | Other(i32), |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | fn expectIntType(a: i32, b: i32, c: bool, d: bool) throws (TestError) {} |
test/tests/edge.cases.6.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Standalone repro: writing a record into a static slice clobbers the slice header. |
|
| 3 | 3 | ||
| 4 | - | record Entry { |
|
| 4 | + | record Entry: Copy { |
|
| 5 | 5 | a: u32, |
|
| 6 | 6 | b: u32, |
|
| 7 | 7 | c: u32, |
|
| 8 | 8 | d: u32, |
|
| 9 | 9 | e: u32, |
| 22 | 22 | r: u32, |
|
| 23 | 23 | s: u32, |
|
| 24 | 24 | t: u32, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | record Analyzer { |
|
| 27 | + | record Analyzer: Copy { |
|
| 28 | 28 | pad0: u32, |
|
| 29 | 29 | pad1: u32, |
|
| 30 | 30 | entries: *mut [Entry], |
|
| 31 | 31 | len: u32, |
|
| 32 | 32 | } |
test/tests/edge.cases.7.addr.bug.rad
+1 -1
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | record PtrHolder { |
|
| 3 | + | record PtrHolder: Copy { |
|
| 4 | 4 | ptr: *mut i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | static target: i32 = 10; |
|
| 8 | 8 | static holder: PtrHolder = undefined; |
test/tests/edge.cases.8.bug.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! When a nested record field from a static variable is passed by value, |
|
| 3 | 3 | //! we only the base address and ignores the offset, |
|
| 4 | 4 | //! causing the function to receive the wrong data. |
|
| 5 | 5 | ||
| 6 | - | record Inner { |
|
| 6 | + | record Inner: Copy { |
|
| 7 | 7 | value: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Outer { |
|
| 10 | + | record Outer: Copy { |
|
| 11 | 11 | padding: i32, |
|
| 12 | 12 | inner: Inner, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | static global: Outer = undefined; |
test/tests/edge.cases.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | export record Scanner { |
|
| 3 | + | export record Scanner: Copy { |
|
| 4 | 4 | source: *[u8], |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | fn peek(s: *Scanner) -> i32 { |
|
| 8 | 8 | assert 0 + 0 <= s.source.len; |
test/tests/error.basic.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union MyError { A, B, C } |
|
| 3 | + | union MyError: Copy { A, B, C } |
|
| 4 | 4 | ||
| 5 | 5 | @default fn main() -> u32 { |
|
| 6 | 6 | let err: MyError = MyError::B; |
|
| 7 | 7 | ||
| 8 | 8 | if err == MyError::B { |
test/tests/error.catch.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union TestError { Fail } |
|
| 3 | + | union TestError: Copy { Fail } |
|
| 4 | 4 | static PTR_VALUE: i32 = 7; |
|
| 5 | 5 | ||
| 6 | 6 | @default fn main() -> u32 { |
|
| 7 | 7 | // Catch block with early return |
|
| 8 | 8 | let val1: u32 = catchWithReturn(true); |
test/tests/error.catch.return.rad
+1 -1
| 1 | 1 | /// Error type for fallible functions. |
|
| 2 | - | union Error { Boom } |
|
| 2 | + | union Error: Copy { Boom } |
|
| 3 | 3 | ||
| 4 | 4 | /// Return a value or throw an error based on the flag. |
|
| 5 | 5 | fn fallible(flag: bool) -> i32 throws (Error) { |
|
| 6 | 6 | if flag { |
|
| 7 | 7 | throw Error::Boom(); |
test/tests/error.multi.basic.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test basic multi-error throw and catch. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A } |
|
| 5 | - | union ErrB { B } |
|
| 4 | + | union ErrA: Copy { A } |
|
| 5 | + | union ErrB: Copy { B } |
|
| 6 | 6 | ||
| 7 | 7 | fn failA() -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | throw ErrA::A(); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.multi.catch.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test catch {} (no binding) with multi-error callee. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A } |
|
| 5 | - | union ErrB { B } |
|
| 4 | + | union ErrA: Copy { A } |
|
| 5 | + | union ErrB: Copy { B } |
|
| 6 | 6 | ||
| 7 | 7 | fn failA() -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | throw ErrA::A(); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.multi.catch.typed.binding.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test typed multi-catch with payload extraction via bindings. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A(i32) } |
|
| 5 | - | union ErrB { B(i32) } |
|
| 4 | + | union ErrA: Copy { A(i32) } |
|
| 5 | + | union ErrB: Copy { B(i32) } |
|
| 6 | 6 | ||
| 7 | 7 | fn failA(v: i32) -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | throw ErrA::A(v); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.multi.catch.typed.catchall.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test typed catch with a catch-all fallback. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A(i32) } |
|
| 5 | - | union ErrB { B(i32) } |
|
| 6 | - | union ErrC { C } |
|
| 4 | + | union ErrA: Copy { A(i32) } |
|
| 5 | + | union ErrB: Copy { B(i32) } |
|
| 6 | + | union ErrC: Copy { C } |
|
| 7 | 7 | ||
| 8 | 8 | fn failA() -> i32 throws (ErrA, ErrB, ErrC) { |
|
| 9 | 9 | throw ErrA::A(10); |
|
| 10 | 10 | } |
|
| 11 | 11 |
test/tests/error.multi.catch.typed.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test typed multi-catch: catch e as ErrA {} catch e as ErrB {}. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A(i32) } |
|
| 5 | - | union ErrB { B(i32) } |
|
| 4 | + | union ErrA: Copy { A(i32) } |
|
| 5 | + | union ErrB: Copy { B(i32) } |
|
| 6 | 6 | ||
| 7 | 7 | fn failA() -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | throw ErrA::A(10); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.multi.propagate.multi.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test propagating a multi-throw callee via plain `try`. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A } |
|
| 5 | - | union ErrB { B } |
|
| 4 | + | union ErrA: Copy { A } |
|
| 5 | + | union ErrB: Copy { B } |
|
| 6 | 6 | ||
| 7 | 7 | fn inner(flag: i32) -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | if flag == 1 { |
|
| 9 | 9 | throw ErrA::A(); |
|
| 10 | 10 | } |
test/tests/error.multi.propagate.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test error propagation with multi-error and subset throw lists. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A } |
|
| 5 | - | union ErrB { B } |
|
| 4 | + | union ErrA: Copy { A } |
|
| 5 | + | union ErrB: Copy { B } |
|
| 6 | 6 | ||
| 7 | 7 | fn innerA() -> i32 throws (ErrA) { |
|
| 8 | 8 | throw ErrA::A(); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.multi.try.optional.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test try? with multi-error callee. |
|
| 3 | 3 | ||
| 4 | - | union ErrA { A } |
|
| 5 | - | union ErrB { B } |
|
| 4 | + | union ErrA: Copy { A } |
|
| 5 | + | union ErrB: Copy { B } |
|
| 6 | 6 | ||
| 7 | 7 | fn failA() -> i32 throws (ErrA, ErrB) { |
|
| 8 | 8 | throw ErrA::A(); |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/error.try.bang.success.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union PanicError { Boom } |
|
| 3 | + | union PanicError: Copy { Boom } |
|
| 4 | 4 | ||
| 5 | 5 | fn makeOk(value: u32) -> u32 throws (PanicError) { |
|
| 6 | 6 | return value; |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/error.try.catch.binding.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union TestError { Boom, Bust } |
|
| 3 | + | union TestError: Copy { Boom, Bust } |
|
| 4 | 4 | ||
| 5 | 5 | @default fn main() -> u32 { |
|
| 6 | 6 | // Test catching and using the error value. |
|
| 7 | 7 | let mut caught: u32 = 0; |
|
| 8 | 8 | try failWithBoom() catch e { |
test/tests/error.try.optional.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test try? expressions converting errors to optionals. |
|
| 3 | 3 | ||
| 4 | - | union TestError { Boom, Bust } |
|
| 4 | + | union TestError: Copy { Boom, Bust } |
|
| 5 | 5 | ||
| 6 | 6 | fn returnsOk() -> u32 throws (TestError) { |
|
| 7 | 7 | return 42; |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | fn returnsErr() -> u32 throws (TestError) { |
|
| 11 | 11 | throw TestError::Boom; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | record Point { |
|
| 14 | + | record Point: Copy { |
|
| 15 | 15 | x: u32, |
|
| 16 | 16 | y: u32, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | fn returnsOkPoint() -> Point throws (TestError) { |
test/tests/error.try.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test result-based error handling with try/throw. |
|
| 3 | 3 | ||
| 4 | - | union TestError { Boom, Bust } |
|
| 4 | + | union TestError: Copy { Boom, Bust } |
|
| 5 | 5 | ||
| 6 | - | record ResultSink { |
|
| 6 | + | record ResultSink: Copy { |
|
| 7 | 7 | last: u32, |
|
| 8 | 8 | count: u32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn returnsOk() -> u32 throws (TestError) { |
test/tests/field.aggregate.rad
+5 -5
| 1 | 1 | /// Test field access where the field is an aggregate type. |
|
| 2 | 2 | /// Tests record, union, and slice fields. |
|
| 3 | 3 | ||
| 4 | - | record Inner { x: i32, y: i32 } |
|
| 5 | - | union MaybeInt { None, Some(i32) } |
|
| 4 | + | record Inner: Copy { x: i32, y: i32 } |
|
| 5 | + | union MaybeInt: Copy { None, Some(i32) } |
|
| 6 | 6 | ||
| 7 | - | record HasRecord { inner: Inner, z: i32 } |
|
| 8 | - | record HasUnion { maybe: MaybeInt, z: i32 } |
|
| 9 | - | record HasSlice { data: *[i32], z: i32 } |
|
| 7 | + | record HasRecord: Copy { inner: Inner, z: i32 } |
|
| 8 | + | record HasUnion: Copy { maybe: MaybeInt, z: i32 } |
|
| 9 | + | record HasSlice: Copy { data: *[i32], z: i32 } |
|
| 10 | 10 | ||
| 11 | 11 | /// Access a record field and read from it. |
|
| 12 | 12 | fn accessRecordField() -> i32 { |
|
| 13 | 13 | let r = HasRecord { inner: Inner { x: 10, y: 20 }, z: 30 }; |
|
| 14 | 14 | return r.inner.y; |
test/tests/fn.callback.nested.rad
+2 -2
| 1 | 1 | //! Test for callback with nested function call inside. |
|
| 2 | 2 | //! This exercises the register save/restore logic when a callback makes |
|
| 3 | 3 | //! a function call that uses caller-saved registers. |
|
| 4 | 4 | //! returns: 0 |
|
| 5 | 5 | ||
| 6 | - | record Reg { n: u32 } |
|
| 6 | + | record Reg: Copy { n: u32 } |
|
| 7 | 7 | ||
| 8 | - | union Val { |
|
| 8 | + | union Val: Copy { |
|
| 9 | 9 | Reg(Reg), |
|
| 10 | 10 | Imm(i32), |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn maxRegNum(n: u32, current: u32) -> u32 { |
test/tests/for.else.continue.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test `else continue` in a for loop with let-else. |
|
| 3 | 3 | ||
| 4 | - | record Field { |
|
| 4 | + | record Field: Copy { |
|
| 5 | 5 | name: ?*[u8], |
|
| 6 | 6 | value: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn findField(fields: *[Field], target: *[u8]) -> ?i32 { |
test/tests/large.blit.store.rad
+1 -1
| 11 | 11 | //! |
|
| 12 | 12 | //! 2. Blit (memcpy) offset overflow: copying structs larger than ~2047 bytes |
|
| 13 | 13 | //! caused the offset in the load/store loop to exceed the 12-bit immediate. |
|
| 14 | 14 | //! Fixed by periodically advancing the base registers in the blit loop. |
|
| 15 | 15 | ||
| 16 | - | record Big { |
|
| 16 | + | record Big: Copy { |
|
| 17 | 17 | a: [u8; 2200], |
|
| 18 | 18 | tag: i32, |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Store at offset 2200 (> MAX_IMM) into a record field. |
test/tests/let.copy.semantics.rad
+2 -2
| 1 | 1 | /// Tests for copy semantics in let bindings. |
|
| 2 | 2 | /// Aggregates with storage (variables, field access, subscript, deref) need copying. |
|
| 3 | 3 | /// Temporaries (literals, call results) can be adopted directly. |
|
| 4 | 4 | ||
| 5 | - | record Point { x: i32, y: i32 } |
|
| 6 | - | record Outer { inner: Point, z: i32 } |
|
| 5 | + | record Point: Copy { x: i32, y: i32 } |
|
| 6 | + | record Outer: Copy { inner: Point, z: i32 } |
|
| 7 | 7 | ||
| 8 | 8 | /// Returns an aggregate - caller should NOT copy. |
|
| 9 | 9 | fn makePoint() -> Point { |
|
| 10 | 10 | return Point { x: 1, y: 2 }; |
|
| 11 | 11 | } |
test/tests/let.guard.rad
+1 -1
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | ||
| 3 | - | union Response { |
|
| 3 | + | union Response: Copy { |
|
| 4 | 4 | success(i32), |
|
| 5 | 5 | failure, |
|
| 6 | 6 | other { message: *[u8] }, |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/literal.slice.record.rad
+1 -1
| 1 | 1 | /// Test slice literal with record elements. |
|
| 2 | - | record Point { x: i32, y: i32 } |
|
| 2 | + | record Point: Copy { x: i32, y: i32 } |
|
| 3 | 3 | ||
| 4 | 4 | fn localSliceOfRecords() -> i32 { |
|
| 5 | 5 | let s: *[Point] = &[Point { x: 5, y: 6 }, Point { x: 7, y: 8 }]; |
|
| 6 | 6 | return s[0].x + s[1].y; |
|
| 7 | 7 | } |
test/tests/load.u32.high.rad
+1 -1
| 5 | 5 | //! Regression test: `lw` sign-extends on RV64, producing a negative |
|
| 6 | 6 | //! 64-bit value for u32 values >= 0x80000000. The correct instruction |
|
| 7 | 7 | //! is `lwu` which zero-extends. |
|
| 8 | 8 | ||
| 9 | 9 | /// A u32 value with bit 31 set, stored in a struct to force a memory load. |
|
| 10 | - | record Pair { |
|
| 10 | + | record Pair: Copy { |
|
| 11 | 11 | lo: u32, |
|
| 12 | 12 | hi: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Test that a u32 field with bit 31 set compares correctly. |
test/tests/loc.addr.offset.bug.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that LOC_ADDR values with non-zero offsets work correctly |
|
| 3 | 3 | //! for pass-by-ref types (structs). |
|
| 4 | 4 | ||
| 5 | - | record Inner { |
|
| 5 | + | record Inner: Copy { |
|
| 6 | 6 | value: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Outer { |
|
| 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 | 14 | static outer: Outer = undefined; |
test/tests/loc.addr.opt.to.opt.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test assigning one optional to another when destination is LOC_ADDR |
|
| 3 | 3 | //! with offset. |
|
| 4 | 4 | ||
| 5 | - | record Container { |
|
| 5 | + | record Container: Copy { |
|
| 6 | 6 | pad: i32, // offset 0 |
|
| 7 | 7 | opt: ?i32, // offset 4 |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | static container: Container = undefined; |
test/tests/loc.addr.optional.assign.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test assigning to an optional field stored at LOC_ADDR with offset. |
|
| 3 | 3 | ||
| 4 | - | record Container { |
|
| 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 | 9 | static container: Container = undefined; |
test/tests/loc.addr.record.assign.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test assigning to a record field that is itself a record |
|
| 3 | 3 | //! when the parent is stored at LOC_ADDR (static). |
|
| 4 | 4 | ||
| 5 | - | record Inner { |
|
| 5 | + | record Inner: Copy { |
|
| 6 | 6 | value: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Outer { |
|
| 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 | 14 | static outer: Outer = undefined; |
test/tests/loop.complex.flow.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test loop with complex control flow inside the body. |
|
| 3 | 3 | //! This mirrors patterns in the lowerer where loops contain |
|
| 4 | 4 | //! match statements, function calls, error handling, etc. |
|
| 5 | 5 | ||
| 6 | - | union Result { |
|
| 6 | + | union Result: Copy { |
|
| 7 | 7 | Ok(i32), |
|
| 8 | 8 | Err(i32), |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn maybeAdd(x: i32, y: i32) -> Result { |
test/tests/loop.whilelet.union.rad
+1 -1
| 1 | 1 | /// Simple union for testing while-let with payload binding. |
|
| 2 | - | union Option { None, Some(u32) } |
|
| 2 | + | union Option: Copy { None, Some(u32) } |
|
| 3 | 3 | ||
| 4 | 4 | /// Sum values from a linked iteration using while-let case with binding. |
|
| 5 | 5 | fn sumWhileLet(x: Option) -> u32 { |
|
| 6 | 6 | let mut opt = x; |
|
| 7 | 7 | let mut sum: u32 = 0; |
test/tests/lower.const.record.ident.rad
+2 -2
| 1 | - | record Reg(u8); |
|
| 1 | + | record Reg: Copy(u8); |
|
| 2 | 2 | ||
| 3 | 3 | constant A0: Reg = Reg(10); |
|
| 4 | 4 | constant A1: Reg = Reg(11); |
|
| 5 | 5 | ||
| 6 | - | record Entry { |
|
| 6 | + | record Entry: Copy { |
|
| 7 | 7 | name: *[u8], |
|
| 8 | 8 | reg: Reg, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | constant ENTRIES: [Entry; 2] = [ |
test/tests/lower.private.union.const.rad
+2 -2
| 1 | - | union Kind { |
|
| 1 | + | union Kind: Copy { |
|
| 2 | 2 | A, |
|
| 3 | 3 | B, |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | - | record Entry { |
|
| 6 | + | record Entry: Copy { |
|
| 7 | 7 | name: *[u8], |
|
| 8 | 8 | kind: Kind, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | constant ENTRIES: [Entry; 2] = [ |
test/tests/lower.record.scalar.record.const.rad
+2 -2
| 1 | - | record Reg(u8); |
|
| 1 | + | record Reg: Copy(u8); |
|
| 2 | 2 | ||
| 3 | - | record Entry { |
|
| 3 | + | record Entry: Copy { |
|
| 4 | 4 | name: *[u8], |
|
| 5 | 5 | reg: Reg, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | constant ENTRIES: [Entry; 2] = [ |
test/tests/match.array.rad
+1 -1
| 45 | 45 | } |
|
| 46 | 46 | } |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Array pattern nested inside union variant. |
|
| 50 | - | union Shape { |
|
| 50 | + | union Shape: Copy { |
|
| 51 | 51 | Point { coords: [i32; 2] }, |
|
| 52 | 52 | Line { start: [i32; 2], end: [i32; 2] }, |
|
| 53 | 53 | None, |
|
| 54 | 54 | } |
|
| 55 | 55 |
test/tests/match.char.rad
+1 -1
| 11 | 11 | else => { return 0; } |
|
| 12 | 12 | } |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Character pattern nested inside a union variant field. |
|
| 16 | - | union Token { |
|
| 16 | + | union Token: Copy { |
|
| 17 | 17 | Punct { ch: u8, pos: i32 }, |
|
| 18 | 18 | Eof, |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | fn tokenVal(t: Token) -> i32 { |
test/tests/match.multi.seal.rad
+1 -1
| 2 | 2 | //! Regression test: intermediate arm blocks in multi-pattern matches must be |
|
| 3 | 3 | //! sealed so that SSA variables flow correctly through single-predecessor |
|
| 4 | 4 | //! optimization. Without sealing, block parameters are created but never |
|
| 5 | 5 | //! resolved, causing values to be read from wrong registers. |
|
| 6 | 6 | ||
| 7 | - | union Kind { A, B, C, D, E } |
|
| 7 | + | union Kind: Copy { A, B, C, D, E } |
|
| 8 | 8 | ||
| 9 | 9 | fn classify(k: Kind, y: i32) -> i32 { |
|
| 10 | 10 | match k { |
|
| 11 | 11 | case Kind::A, Kind::B, Kind::C => { |
|
| 12 | 12 | return y + 10; |
test/tests/match.multi.survive.rad
+2 -2
| 7 | 7 | //! the fall-through (else) path. |
|
| 8 | 8 | //! |
|
| 9 | 9 | //! This test catches the bug by requiring a value (`state`) to survive |
|
| 10 | 10 | //! through multiple match arms and into the `else` branch. |
|
| 11 | 11 | ||
| 12 | - | union Kind { |
|
| 12 | + | union Kind: Copy { |
|
| 13 | 13 | Alpha, |
|
| 14 | 14 | Beta, |
|
| 15 | 15 | Gamma, |
|
| 16 | 16 | Delta, |
|
| 17 | 17 | Epsilon, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | record State { |
|
| 20 | + | record State: Copy { |
|
| 21 | 21 | value: i32, |
|
| 22 | 22 | kind: Kind, |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | fn classify(s: State) -> i32 { |
test/tests/match.mutref.push.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test pushing items through a mutable reference obtained via match. |
|
| 3 | 3 | ||
| 4 | - | record U32List { |
|
| 4 | + | record U32List: Copy { |
|
| 5 | 5 | data: *mut [u32], |
|
| 6 | 6 | len: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Sealed { |
|
| 9 | + | union Sealed: Copy { |
|
| 10 | 10 | No { items: U32List }, |
|
| 11 | 11 | Yes, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | fn pushItem(list: *mut U32List, value: u32) { |
test/tests/match.mutref.union.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test matching on a mutable reference to a union field and mutating |
|
| 3 | 3 | //! the payload in place. |
|
| 4 | 4 | ||
| 5 | - | union State { |
|
| 5 | + | union State: Copy { |
|
| 6 | 6 | A { count: u32 }, |
|
| 7 | 7 | B, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Data { |
|
| 10 | + | record Data: Copy { |
|
| 11 | 11 | state: State, |
|
| 12 | 12 | value: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn process(d: *mut Data) -> u32 { |
test/tests/match.nested.call.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns with tuple-style (Call) union variants. |
|
| 3 | 3 | ||
| 4 | - | union Dir { North, South } |
|
| 5 | - | union Opt { Some(i32), None } |
|
| 4 | + | union Dir: Copy { North, South } |
|
| 5 | + | union Opt: Copy { Some(i32), None } |
|
| 6 | 6 | ||
| 7 | 7 | /// Tuple-style variant with integer literal nested pattern. |
|
| 8 | 8 | fn checkOpt(o: Opt) -> i32 { |
|
| 9 | 9 | match o { |
|
| 10 | 10 | case Opt::Some(42) => { |
| 33 | 33 | else { return -1; }; |
|
| 34 | 34 | return x; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Nested union inside tuple-style variant. |
|
| 38 | - | union Wrapper { Wrap(Dir), Empty } |
|
| 38 | + | union Wrapper: Copy { Wrap(Dir), Empty } |
|
| 39 | 39 | ||
| 40 | 40 | fn extractDir(w: Wrapper) -> i32 { |
|
| 41 | 41 | match w { |
|
| 42 | 42 | case Wrapper::Wrap(Dir::North) => { |
|
| 43 | 43 | return 10; |
test/tests/match.nested.deep.rad
+6 -6
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test deeply nested patterns: three levels of record nesting, |
|
| 3 | 3 | //! and nested unions inside records inside unions. |
|
| 4 | 4 | ||
| 5 | - | record Leaf { |
|
| 5 | + | record Leaf: Copy { |
|
| 6 | 6 | val: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Branch { |
|
| 9 | + | record Branch: Copy { |
|
| 10 | 10 | leaf: Leaf, |
|
| 11 | 11 | extra: i32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | union Tree { |
|
| 14 | + | union Tree: Copy { |
|
| 15 | 15 | Node { branch: Branch, tag: i32 }, |
|
| 16 | 16 | Empty, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | /// Three levels deep: union -> record -> record -> record. |
| 27 | 27 | } |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Nested union inside a record inside a union. |
|
| 32 | - | union Color { Red, Green, Blue } |
|
| 32 | + | union Color: Copy { Red, Green, Blue } |
|
| 33 | 33 | ||
| 34 | - | record Pixel { |
|
| 34 | + | record Pixel: Copy { |
|
| 35 | 35 | color: Color, |
|
| 36 | 36 | brightness: i32, |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | union Canvas { |
|
| 39 | + | union Canvas: Copy { |
|
| 40 | 40 | Filled { pixel: Pixel }, |
|
| 41 | 41 | Blank, |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | fn getColor(cv: Canvas) -> i32 { |
test/tests/match.nested.deref.rad
+6 -6
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns through pointer dereferences (auto-deref). |
|
| 3 | 3 | ||
| 4 | - | union Inner { |
|
| 4 | + | union Inner: Copy { |
|
| 5 | 5 | A(i32), |
|
| 6 | 6 | B, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Outer { |
|
| 9 | + | union Outer: Copy { |
|
| 10 | 10 | Some(*Inner), |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Match nested union variant through pointer dereference in match/case. |
| 40 | 40 | else { return -1; }; |
|
| 41 | 41 | return x; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Record with pointer field and nested pattern through deref. |
|
| 45 | - | record Container { |
|
| 45 | + | record Container: Copy { |
|
| 46 | 46 | inner: *Inner, |
|
| 47 | 47 | tag: i32, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | - | union Boxed { |
|
| 50 | + | union Boxed: Copy { |
|
| 51 | 51 | Some { c: Container }, |
|
| 52 | 52 | None, |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Nested record with auto-deref on a pointer field. |
| 87 | 87 | } |
|
| 88 | 88 | return 0; |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | 91 | /// Auto-deref through pointer to record in a record field. |
|
| 92 | - | record Point { |
|
| 92 | + | record Point: Copy { |
|
| 93 | 93 | x: i32, |
|
| 94 | 94 | y: i32, |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | - | union Holder { |
|
| 97 | + | union Holder: Copy { |
|
| 98 | 98 | Ptr { p: *Point, z: i32 }, |
|
| 99 | 99 | Empty, |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | 102 | fn derefRecordField(h: Holder) -> i32 { |
test/tests/match.nested.guard.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns combined with guards. |
|
| 3 | 3 | ||
| 4 | - | record Pair { |
|
| 4 | + | record Pair: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Opt { |
|
| 9 | + | union Opt: Copy { |
|
| 10 | 10 | Some { pair: Pair }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Nested pattern with guard on bound variable. |
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Nested union with guard. |
|
| 30 | - | union Tag { X, Y } |
|
| 30 | + | union Tag: Copy { X, Y } |
|
| 31 | 31 | ||
| 32 | - | union Box { |
|
| 32 | + | union Box: Copy { |
|
| 33 | 33 | Item { tag: Tag, val: i32 }, |
|
| 34 | 34 | Empty, |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | fn guardedNested(bx: Box) -> i32 { |
test/tests/match.nested.iflet.guard.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns combined with guards in if-let-case. |
|
| 3 | 3 | ||
| 4 | - | record Pair { |
|
| 4 | + | record Pair: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Opt { |
|
| 9 | + | union Opt: Copy { |
|
| 10 | 10 | Some { pair: Pair }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Nested record pattern with guard on bound variable. |
| 18 | 18 | } |
|
| 19 | 19 | return -1; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Nested union variant with guard. |
|
| 23 | - | union Dir { North, South } |
|
| 23 | + | union Dir: Copy { North, South } |
|
| 24 | 24 | ||
| 25 | - | union Command { |
|
| 25 | + | union Command: Copy { |
|
| 26 | 26 | Move { dir: Dir, speed: i32 }, |
|
| 27 | 27 | Stop, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | fn fastNorth(c: Command) -> i32 { |
test/tests/match.nested.iflet.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns in if-let-case and let-else contexts. |
|
| 3 | 3 | ||
| 4 | - | record Inner { |
|
| 4 | + | record Inner: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Wrapper { |
|
| 9 | + | union Wrapper: Copy { |
|
| 10 | 10 | Some { inner: Inner, z: i32 }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Nested if-let-case. |
| 18 | 18 | } |
|
| 19 | 19 | return -1; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Nested union variant in if-let-case. |
|
| 23 | - | union Kind { A, B } |
|
| 23 | + | union Kind: Copy { A, B } |
|
| 24 | 24 | ||
| 25 | - | union Tagged { |
|
| 25 | + | union Tagged: Copy { |
|
| 26 | 26 | Item { kind: Kind, val: i32 }, |
|
| 27 | 27 | Empty, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | fn ifLetNestedUnion(t: Tagged) -> i32 { |
test/tests/match.nested.letelse.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns in let-else statements. |
|
| 3 | 3 | ||
| 4 | - | record Inner { |
|
| 4 | + | record Inner: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Wrapper { |
|
| 9 | + | union Wrapper: Copy { |
|
| 10 | 10 | Some { inner: Inner, z: i32 }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Nested record destructuring in let-else. |
test/tests/match.nested.letelse.union.rad
+5 -5
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested union variant patterns in let-else statements. |
|
| 3 | 3 | ||
| 4 | - | union Dir { North, South } |
|
| 4 | + | union Dir: Copy { North, South } |
|
| 5 | 5 | ||
| 6 | - | union Command { |
|
| 6 | + | union Command: Copy { |
|
| 7 | 7 | Move { dir: Dir, speed: i32 }, |
|
| 8 | 8 | Stop, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | /// Nested union variant in let-else. |
| 14 | 14 | else { return -1; }; |
|
| 15 | 15 | return speed; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | /// Nested union variant in let-else, deeper nesting. |
|
| 19 | - | record Inner { |
|
| 19 | + | record Inner: Copy { |
|
| 20 | 20 | x: i32, |
|
| 21 | 21 | y: i32, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | union Wrapper { |
|
| 24 | + | union Wrapper: Copy { |
|
| 25 | 25 | Some { inner: Inner, z: i32 }, |
|
| 26 | 26 | None, |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | - | union Box { |
|
| 29 | + | union Box: Copy { |
|
| 30 | 30 | Filled { w: Wrapper, tag: i32 }, |
|
| 31 | 31 | Empty, |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | fn extractInner(b: Box) -> i32 { |
test/tests/match.nested.literal.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test literal patterns (integers, booleans) nested inside union variant fields. |
|
| 3 | 3 | ||
| 4 | - | union Action { |
|
| 4 | + | union Action: Copy { |
|
| 5 | 5 | Set { code: i32, value: i32 }, |
|
| 6 | 6 | Toggle { flag: bool }, |
|
| 7 | 7 | None, |
|
| 8 | 8 | } |
|
| 9 | 9 |
| 53 | 53 | } |
|
| 54 | 54 | return false; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Multiple literal fields in the same pattern. |
|
| 58 | - | union Pair { |
|
| 58 | + | union Pair: Copy { |
|
| 59 | 59 | Both { a: i32, b: i32 }, |
|
| 60 | 60 | Empty, |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | fn matchBothLiterals(p: Pair) -> i32 { |
test/tests/match.nested.multi.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test multiple nested refining fields in the same record pattern. |
|
| 3 | 3 | ||
| 4 | - | union Dir { North, South, East } |
|
| 5 | - | union Mode { Fast, Slow } |
|
| 4 | + | union Dir: Copy { North, South, East } |
|
| 5 | + | union Mode: Copy { Fast, Slow } |
|
| 6 | 6 | ||
| 7 | - | union Command { |
|
| 7 | + | union Command: Copy { |
|
| 8 | 8 | Move { dir: Dir, mode: Mode, speed: i32 }, |
|
| 9 | 9 | Stop, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Two union-typed fields refined simultaneously. |
test/tests/match.nested.pattern.rad
+12 -12
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns in match/case statements. |
|
| 3 | 3 | ||
| 4 | - | record Inner { |
|
| 4 | + | record Inner: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Outer { |
|
| 9 | + | union Outer: Copy { |
|
| 10 | 10 | A { inner: Inner, z: i32 }, |
|
| 11 | 11 | B, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Match a union variant and destructure its nested record field. |
| 42 | 42 | } |
|
| 43 | 43 | } |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Multi-level nesting: three records deep. |
|
| 47 | - | record Deep { |
|
| 47 | + | record Deep: Copy { |
|
| 48 | 48 | val: i32, |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | record Mid { |
|
| 51 | + | record Mid: Copy { |
|
| 52 | 52 | deep: Deep, |
|
| 53 | 53 | extra: i32, |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | union Wrapper { |
|
| 56 | + | union Wrapper: Copy { |
|
| 57 | 57 | W { mid: Mid, tag: i32 }, |
|
| 58 | 58 | Empty, |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | fn testDeepNesting(w: Wrapper) -> i32 { |
| 68 | 68 | } |
|
| 69 | 69 | } |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Nested union inside a union variant. |
|
| 73 | - | union Direction { |
|
| 73 | + | union Direction: Copy { |
|
| 74 | 74 | North, |
|
| 75 | 75 | South, |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | union Command { |
|
| 78 | + | union Command: Copy { |
|
| 79 | 79 | Move { dir: Direction, speed: i32 }, |
|
| 80 | 80 | Stop, |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | 83 | fn testNestedUnionLiteral(c: Command) -> i32 { |
| 93 | 93 | } |
|
| 94 | 94 | } |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | 97 | /// Mixed record and union nesting with multiple fields. |
|
| 98 | - | record Point { |
|
| 98 | + | record Point: Copy { |
|
| 99 | 99 | x: i32, |
|
| 100 | 100 | y: i32, |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | - | union Shape { |
|
| 103 | + | union Shape: Copy { |
|
| 104 | 104 | Circle { center: Point, radius: i32 }, |
|
| 105 | 105 | Rect { origin: Point, size: Point }, |
|
| 106 | 106 | None, |
|
| 107 | 107 | } |
|
| 108 | 108 |
| 119 | 119 | } |
|
| 120 | 120 | } |
|
| 121 | 121 | } |
|
| 122 | 122 | ||
| 123 | 123 | /// Nested union variant pattern inside record field. |
|
| 124 | - | union Pair { |
|
| 124 | + | union Pair: Copy { |
|
| 125 | 125 | Val { a: i32, b: i32 }, |
|
| 126 | 126 | Empty, |
|
| 127 | 127 | } |
|
| 128 | 128 | ||
| 129 | - | record Container { |
|
| 129 | + | record Container: Copy { |
|
| 130 | 130 | pair: Pair, |
|
| 131 | 131 | label: i32, |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | - | union Boxed { |
|
| 134 | + | union Boxed: Copy { |
|
| 135 | 135 | Some { c: Container }, |
|
| 136 | 136 | None, |
|
| 137 | 137 | } |
|
| 138 | 138 | ||
| 139 | 139 | fn testNestedUnionInRecord(bx: Boxed) -> i32 { |
test/tests/match.nested.record.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested record destructuring inside union variant patterns. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Rect { |
|
| 9 | + | record Rect: Copy { |
|
| 10 | 10 | origin: Point, |
|
| 11 | 11 | size: Point, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | union Shape { |
|
| 14 | + | union Shape: Copy { |
|
| 15 | 15 | Circle { center: Point, radius: i32 }, |
|
| 16 | 16 | Rectangle { rect: Rect }, |
|
| 17 | 17 | None, |
|
| 18 | 18 | } |
|
| 19 | 19 |
test/tests/match.nested.union.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested union variant patterns inside record fields. |
|
| 3 | 3 | ||
| 4 | - | union Dir { North, South, East, West } |
|
| 4 | + | union Dir: Copy { North, South, East, West } |
|
| 5 | 5 | ||
| 6 | - | union Command { |
|
| 6 | + | union Command: Copy { |
|
| 7 | 7 | Move { dir: Dir, speed: i32 }, |
|
| 8 | 8 | Turn { dir: Dir }, |
|
| 9 | 9 | Stop, |
|
| 10 | 10 | } |
|
| 11 | 11 |
test/tests/match.nested.whilelet.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested patterns in while-let loops. |
|
| 3 | 3 | ||
| 4 | - | record Pair { |
|
| 4 | + | record Pair: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Opt { |
|
| 9 | + | union Opt: Copy { |
|
| 10 | 10 | Some { pair: Pair }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | fn get(items: *[Opt], idx: u32) -> Opt { |
| 38 | 38 | set i = i + 1; |
|
| 39 | 39 | } |
|
| 40 | 40 | return sum; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | - | union Dir { North, South } |
|
| 43 | + | union Dir: Copy { North, South } |
|
| 44 | 44 | ||
| 45 | - | union Command { |
|
| 45 | + | union Command: Copy { |
|
| 46 | 46 | Move { dir: Dir, speed: i32 }, |
|
| 47 | 47 | Stop, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | fn getCmd(items: *[Command], idx: u32) -> Command { |
test/tests/match.record.pattern.rad
+1 -1
| 1 | 1 | /// Match on union variant with record payload and bind fields. |
|
| 2 | - | union Expr { |
|
| 2 | + | union Expr: Copy { |
|
| 3 | 3 | Null, |
|
| 4 | 4 | Pair { first: i32, second: i32 }, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Extract the first field from a Pair variant. |
test/tests/match.string.rad
+1 -1
| 18 | 18 | } |
|
| 19 | 19 | } |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// String pattern nested inside a union variant field. |
|
| 23 | - | union Cmd { |
|
| 23 | + | union Cmd: Copy { |
|
| 24 | 24 | Say { msg: *[u8], count: i32 }, |
|
| 25 | 25 | Quit, |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | fn dispatch(c: Cmd) -> i32 { |
test/tests/match.value.copy.rad
+3 -3
| 5 | 5 | //! |
|
| 6 | 6 | //! The bug: when pattern-matching a union by value, the lowerer returns a |
|
| 7 | 7 | //! pointer into the original union's payload instead of copying it. If the |
|
| 8 | 8 | //! source is overwritten, the binding reads garbage. |
|
| 9 | 9 | ||
| 10 | - | record Pair { |
|
| 10 | + | record Pair: Copy { |
|
| 11 | 11 | a: u32, |
|
| 12 | 12 | b: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | union Data { |
|
| 15 | + | union Data: Copy { |
|
| 16 | 16 | Some { pair: Pair }, |
|
| 17 | 17 | None, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | record Box { |
|
| 20 | + | record Box: Copy { |
|
| 21 | 21 | data: Data, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Test let-case on an aggregate union payload. |
|
| 25 | 25 | /// The Pair inside the union is an aggregate - its binding will be a pointer |
test/tests/match.void.then.or.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Regression test: match on void union followed by 'or' equality check |
|
| 3 | 3 | //! in the else branch. |
|
| 4 | 4 | ||
| 5 | - | union Op { |
|
| 5 | + | union Op: Copy { |
|
| 6 | 6 | Add, |
|
| 7 | 7 | Sub, |
|
| 8 | 8 | Mul, |
|
| 9 | 9 | Div, |
|
| 10 | 10 | And, |
| 12 | 12 | Xor, |
|
| 13 | 13 | Eq, |
|
| 14 | 14 | Ne, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | record Expr { |
|
| 17 | + | record Expr: Copy { |
|
| 18 | 18 | op: Op, |
|
| 19 | 19 | left: i32, |
|
| 20 | 20 | right: i32, |
|
| 21 | 21 | } |
|
| 22 | 22 |
test/tests/memzero.result.bug.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test for result memzero overrun: success payload is tiny, error payload |
|
| 3 | 3 | //! is large, and a guard word sits immediately after the return slot. |
|
| 4 | 4 | ||
| 5 | - | union Err { |
|
| 5 | + | union Err: Copy { |
|
| 6 | 6 | Big([u32; 3]), // 12-byte error payload, forces a large result container |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn fallible(ok: bool) throws (Err) { |
|
| 10 | 10 | if ok { |
test/tests/memzero.union.bug.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test enum with a large variant to force a payload capacity bigger than |
|
| 3 | 3 | //! the small variant we actually store. |
|
| 4 | - | union Payload { |
|
| 4 | + | union Payload: Copy { |
|
| 5 | 5 | Big([u32; 3]), // 12-byte payload |
|
| 6 | 6 | Small(u8), // 1-byte payload |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Frame { |
|
| 9 | + | record Frame: Copy { |
|
| 10 | 10 | guard1: u16, |
|
| 11 | 11 | val: Payload, |
|
| 12 | 12 | guard2: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 |
test/tests/method.basic.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Basic standalone method test. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn (p: *Point) sum() -> i32 { |
test/tests/method.chain.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Method chaining via mutable pointer returns. |
|
| 3 | 3 | ||
| 4 | - | record Builder { |
|
| 4 | + | record Builder: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn (b: *mut Builder) setX(x: i32) -> *mut Builder { |
test/tests/method.multiple.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Multiple methods on the same type. |
|
| 3 | 3 | ||
| 4 | - | record Vec2 { |
|
| 4 | + | record Vec2: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn (v: *Vec2) magnitudeSq() -> i32 { |
test/tests/method.ptr.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Methods called via various pointer indirections. |
|
| 3 | 3 | ||
| 4 | - | record Counter { |
|
| 4 | + | record Counter: Copy { |
|
| 5 | 5 | value: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn (c: *Counter) get() -> i32 { |
|
| 9 | 9 | return c.value; |
test/tests/method.pub.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Public attribute on standalone methods. |
|
| 3 | 3 | ||
| 4 | - | record Foo { |
|
| 4 | + | record Foo: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | export fn (f: *Foo) getX() -> i32 { |
|
| 9 | 9 | return f.x; |
test/tests/method.return.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Methods returning various types and records. |
|
| 3 | 3 | ||
| 4 | - | record Pair { |
|
| 4 | + | record Pair: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn (p: *Pair) sum() -> i32 { |
test/tests/method.throws.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Methods that throw errors. |
|
| 3 | 3 | ||
| 4 | - | union ParseError { |
|
| 4 | + | union ParseError: Copy { |
|
| 5 | 5 | Invalid, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Parser { |
|
| 8 | + | record Parser: Copy { |
|
| 9 | 9 | pos: i32, |
|
| 10 | 10 | len: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn (p: *mut Parser) advance() throws (ParseError) { |
test/tests/method.union.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Standalone methods on union types. |
|
| 3 | 3 | ||
| 4 | - | union Shape { |
|
| 4 | + | union Shape: Copy { |
|
| 5 | 5 | Circle(i32), |
|
| 6 | 6 | Rect { w: i32, h: i32 }, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn (s: *Shape) isCircle() -> bool { |
test/tests/method.with.trait.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Standalone methods coexisting with trait instances on the same type. |
|
| 3 | 3 | ||
| 4 | - | record Widget { |
|
| 4 | + | record Widget: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // Standalone method. |
test/tests/multi.throw.basic.rad
+2 -2
| 1 | 1 | /// Multi-throw: function that can throw two different error types. |
|
| 2 | - | union ErrA { A } |
|
| 3 | - | union ErrB { B } |
|
| 2 | + | union ErrA: Copy { A } |
|
| 3 | + | union ErrB: Copy { B } |
|
| 4 | 4 | ||
| 5 | 5 | fn fallible(flag: i32) -> i32 throws (ErrA, ErrB) { |
|
| 6 | 6 | if flag == 1 { |
|
| 7 | 7 | throw ErrA::A(); |
|
| 8 | 8 | } |
test/tests/multi.throw.catch.typed.rad
+2 -2
| 1 | 1 | /// Multi-error typed catch: switch dispatch on error tag. |
|
| 2 | - | union ErrA { A } |
|
| 3 | - | union ErrB { B } |
|
| 2 | + | union ErrA: Copy { A } |
|
| 3 | + | union ErrB: Copy { B } |
|
| 4 | 4 | ||
| 5 | 5 | fn fallible(flag: i32) -> i32 throws (ErrA, ErrB) { |
|
| 6 | 6 | if flag == 1 { |
|
| 7 | 7 | throw ErrA::A(); |
|
| 8 | 8 | } |
test/tests/multi.throw.propagate.rad
+2 -2
| 1 | 1 | /// Multi-throw propagation: caller propagates errors from callee with subset throw list. |
|
| 2 | - | union ErrA { A } |
|
| 3 | - | union ErrB { B } |
|
| 2 | + | union ErrA: Copy { A } |
|
| 3 | + | union ErrB: Copy { B } |
|
| 4 | 4 | ||
| 5 | 5 | fn inner(flag: bool) -> i32 throws (ErrA) { |
|
| 6 | 6 | if flag { |
|
| 7 | 7 | throw ErrA::A(); |
|
| 8 | 8 | } |
test/tests/mutref.call.result.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Mutable borrow through a field access on a call result. |
|
| 3 | 3 | ||
| 4 | - | record Box { |
|
| 4 | + | record Box: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn idBox(b: *mut Box) -> *mut Box { |
|
| 9 | 9 | return b; |
test/tests/opt.record.eq.rad
+2 -2
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | //! Test equality comparison of optional records. |
|
| 3 | 3 | ||
| 4 | - | record Inner { |
|
| 4 | + | record Inner: Copy { |
|
| 5 | 5 | start: i32, |
|
| 6 | 6 | end: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Outer { |
|
| 9 | + | record Outer: Copy { |
|
| 10 | 10 | item: ?Inner, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | @default fn main() -> bool { |
|
| 14 | 14 | let outerA = Outer { item: Inner { start: 3, end: 9 } }; |
test/tests/opt.record.eq.rev.rad
+1 -1
| 1 | 1 | /// Tests equality between an optional record and a record (?T == T). |
|
| 2 | 2 | ||
| 3 | - | record Id { n: u32 } |
|
| 3 | + | record Id: Copy { n: u32 } |
|
| 4 | 4 | ||
| 5 | 5 | /// Wraps a record in an optional. |
|
| 6 | 6 | fn makeOpt(id: Id) -> ?Id { |
|
| 7 | 7 | return id; |
|
| 8 | 8 | } |
test/tests/opt.record.rad
+2 -2
| 1 | 1 | //! returns: 28 |
|
| 2 | 2 | //! Test optional records with if-let. |
|
| 3 | 3 | ||
| 4 | - | record Vector { |
|
| 4 | + | record Vector: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | z: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Wrapper { |
|
| 10 | + | record Wrapper: Copy { |
|
| 11 | 11 | opt: ?i32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | fn takeOptional(x: ?i32) -> i32 { |
|
| 15 | 15 | if let value = x { |
test/tests/opt.return.nested.rad
+1 -1
| 1 | 1 | //! returns: 99 |
|
| 2 | 2 | ||
| 3 | - | record Pair { |
|
| 3 | + | record Pair: Copy { |
|
| 4 | 4 | x: u32, |
|
| 5 | 5 | y: u32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn pick(flag: bool) -> ?Pair { |
test/tests/opt.return.record.rad
+1 -1
| 1 | 1 | //! returns: 99 |
|
| 2 | 2 | //! Test returning optional records from functions. |
|
| 3 | 3 | ||
| 4 | - | record Vector { |
|
| 4 | + | record Vector: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | z: i32 |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/optional.aggregate.eq.rad
+1 -1
| 1 | 1 | // Test optional aggregate equality (non-pointer optionals) |
|
| 2 | 2 | ||
| 3 | - | record Pair { |
|
| 3 | + | record Pair: Copy { |
|
| 4 | 4 | a: i32, |
|
| 5 | 5 | b: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn optPairEq(a: ?Pair, b: ?Pair) -> bool { |
test/tests/optional.record.value.match.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Matching an optional record against a record value must compile and compare structurally. |
|
| 3 | 3 | ||
| 4 | - | record Pair { |
|
| 4 | + | record Pair: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn classify(value: ?Pair) -> i32 { |
test/tests/parser.call.record.argument.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Box { value: i32 } |
|
| 3 | + | record Box: Copy { value: i32 } |
|
| 4 | 4 | ||
| 5 | 5 | fn isOne(box: Box) -> bool { |
|
| 6 | 6 | return box.value == 1; |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/parser.condition.array.record.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Bracketed array elements may contain record literals in a condition. |
|
| 3 | 3 | ||
| 4 | - | record Box { value: i32 } |
|
| 4 | + | record Box: Copy { value: i32 } |
|
| 5 | 5 | ||
| 6 | 6 | @default fn main() -> i32 { |
|
| 7 | 7 | if [Box { value: 1 }][0].value == 1 { |
|
| 8 | 8 | return 0; |
|
| 9 | 9 | } |
test/tests/parser.condition.subscript.record.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Bracketed subscript expressions may contain record literals in a condition. |
|
| 3 | 3 | ||
| 4 | - | record Index { value: u32 } |
|
| 4 | + | record Index: Copy { value: u32 } |
|
| 5 | 5 | ||
| 6 | 6 | @default fn main() -> i32 { |
|
| 7 | 7 | let values: [i32; 1] = [7]; |
|
| 8 | 8 | if values[Index { value: 0 }.value] == 7 { |
|
| 9 | 9 | return 0; |
test/tests/pointer.copy.edge.case.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union NodeKind { |
|
| 3 | + | union NodeKind: Copy { |
|
| 4 | 4 | Placeholder, |
|
| 5 | 5 | Bool(bool), |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Span { |
|
| 8 | + | record Span: Copy { |
|
| 9 | 9 | length: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Node { |
|
| 12 | + | record Node: Copy { |
|
| 13 | 13 | span: Span, |
|
| 14 | 14 | kind: NodeKind, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | record Parser { |
|
| 17 | + | record Parser: Copy { |
|
| 18 | 18 | nodes: [Node; 1], |
|
| 19 | 19 | count: u32, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | fn makeNode(p: *mut Parser, kind: NodeKind) -> *mut Node { |
test/tests/pointer.slice.index.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Holder { |
|
| 3 | + | record Holder: Copy { |
|
| 4 | 4 | ptr: *[i32], |
|
| 5 | 5 | zero: u32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer-to-slice indexing correctly dereferences the slice header. |
test/tests/pointer.slice.store.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test storing through a pointer to a slice in static storage. |
|
| 3 | 3 | ||
| 4 | - | record Entry { |
|
| 4 | + | record Entry: Copy { |
|
| 5 | 5 | a: u32, |
|
| 6 | 6 | b: u32, |
|
| 7 | 7 | c: u32, |
|
| 8 | 8 | d: u32, |
|
| 9 | 9 | e: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Table { |
|
| 12 | + | record Table: Copy { |
|
| 13 | 13 | entries: *mut [Entry], |
|
| 14 | 14 | len: u32, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | record PtrBox { |
|
| 17 | + | record PtrBox: Copy { |
|
| 18 | 18 | ptr: *mut *mut [Entry], |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | static STORAGE: [Entry; 2] = undefined; |
|
| 22 | 22 | static TABLE: Table = undefined; |
test/tests/prog.cordic.rad
+1 -1
| 20 | 20 | ||
| 21 | 21 | /// Pi/2 in Q16.16. |
|
| 22 | 22 | constant HALF_PI: i32 = 102944; |
|
| 23 | 23 | ||
| 24 | 24 | /// Result record for cos and sin. |
|
| 25 | - | record CosSin { |
|
| 25 | + | record CosSin: Copy { |
|
| 26 | 26 | cos: i32, |
|
| 27 | 27 | sin: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// CORDIC rotation mode: compute cos(angle) and sin(angle). |
test/tests/prog.dijkstra.rad
+2 -2
| 4 | 4 | //! and a min-heap priority queue. Reconstruct paths and verify distances. |
|
| 5 | 5 | ||
| 6 | 6 | constant MAX_NODES: u32 = 16; |
|
| 7 | 7 | constant INF: u32 = 0xFFFFFFFF; |
|
| 8 | 8 | ||
| 9 | - | record HeapEntry { |
|
| 9 | + | record HeapEntry: Copy { |
|
| 10 | 10 | dist: u32, |
|
| 11 | 11 | node: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | record Graph { |
|
| 14 | + | record Graph: Copy { |
|
| 15 | 15 | adj: *mut [[u32; 16]], |
|
| 16 | 16 | dist: *mut [u32], |
|
| 17 | 17 | prev: *mut [i32], |
|
| 18 | 18 | visited: *mut [bool], |
|
| 19 | 19 | numNodes: u32, |
test/tests/prog.eval.rad
+4 -4
| 4 | 4 | //! of tagged union nodes. Build expression trees, evaluate them recursively, |
|
| 5 | 5 | //! and verify results. |
|
| 6 | 6 | ||
| 7 | 7 | /// An expression node. Leaf nodes hold a number. Interior nodes hold an |
|
| 8 | 8 | /// operator and indices (into the nodes array) of their left and right children. |
|
| 9 | - | union Expr { |
|
| 9 | + | union Expr: Copy { |
|
| 10 | 10 | Num(i32), |
|
| 11 | 11 | Add(BinOp), |
|
| 12 | 12 | Sub(BinOp), |
|
| 13 | 13 | Mul(BinOp), |
|
| 14 | 14 | Div(BinOp), |
|
| 15 | 15 | Neg(u32), |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | /// Binary operator: indices of left and right child nodes. |
|
| 19 | - | record BinOp { |
|
| 19 | + | record BinOp: Copy { |
|
| 20 | 20 | left: u32, |
|
| 21 | 21 | right: u32, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// A pool of expression nodes with a count of allocated nodes. |
|
| 25 | - | record Pool { |
|
| 25 | + | record Pool: Copy { |
|
| 26 | 26 | nodes: [Expr; 64], |
|
| 27 | 27 | count: u32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Evaluation error. |
|
| 31 | - | union EvalError { |
|
| 31 | + | union EvalError: Copy { |
|
| 32 | 32 | DivByZero |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Allocate a new node, returning its index. |
|
| 36 | 36 | fn newNode(pool: *mut Pool, expr: Expr) -> u32 { |
test/tests/prog.hanoi.rad
+3 -3
| 6 | 6 | constant NUM_DISKS: u32 = 6; |
|
| 7 | 7 | /// 2^6 - 1 = 63 moves. |
|
| 8 | 8 | constant MAX_MOVES: u32 = 63; |
|
| 9 | 9 | ||
| 10 | 10 | /// A single move: move a disk from one peg to another. |
|
| 11 | - | record Move { |
|
| 11 | + | record Move: Copy { |
|
| 12 | 12 | disk: u32, |
|
| 13 | 13 | from: u32, |
|
| 14 | 14 | to: u32, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Log of all moves performed. |
|
| 18 | - | record MoveLog { |
|
| 18 | + | record MoveLog: Copy { |
|
| 19 | 19 | moves: [Move; 63], |
|
| 20 | 20 | count: u32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Record a move. |
| 65 | 65 | return 0; |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | /// Peg state: array of 3 pegs, each holding up to NUM_DISKS disks. |
|
| 69 | 69 | /// top[p] = number of disks currently on peg p. |
|
| 70 | - | record Pegs { |
|
| 70 | + | record Pegs: Copy { |
|
| 71 | 71 | stacks: [[u32; 6]; 3], |
|
| 72 | 72 | top: [u32; 3], |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | fn pegPush(pegs: *mut Pegs, peg: u32, disk: u32) -> bool { |
test/tests/prog.huffman.rad
+3 -3
| 6 | 6 | constant MAX_SYMBOLS: u32 = 32; |
|
| 7 | 7 | constant MAX_NODES: u32 = 63; |
|
| 8 | 8 | constant MAX_BITS: u32 = 512; |
|
| 9 | 9 | ||
| 10 | 10 | /// A node in the Huffman tree. |
|
| 11 | - | union HNodeKind { |
|
| 11 | + | union HNodeKind: Copy { |
|
| 12 | 12 | /// Leaf node with a symbol index. |
|
| 13 | 13 | Leaf(u32), |
|
| 14 | 14 | /// Interior node (no symbol). |
|
| 15 | 15 | Interior, |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | record HNode { |
|
| 18 | + | record HNode: Copy { |
|
| 19 | 19 | freq: u32, |
|
| 20 | 20 | kind: HNodeKind, |
|
| 21 | 21 | left: u32, |
|
| 22 | 22 | right: u32, |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | constant NIL: u32 = 0xFFFFFFFF; |
|
| 26 | 26 | ||
| 27 | - | record HuffState { |
|
| 27 | + | record HuffState: Copy { |
|
| 28 | 28 | nodes: *mut [HNode], |
|
| 29 | 29 | nodeCount: u32, |
|
| 30 | 30 | heap: *mut [u32], |
|
| 31 | 31 | heapSize: u32, |
|
| 32 | 32 | codeBits: *mut [u32], |
test/tests/prog.linkedlist.rad
+2 -2
| 2 | 2 | //! Linked list via array pool. |
|
| 3 | 3 | //! Implement a singly-linked list using an array of node records |
|
| 4 | 4 | //! (pool allocator pattern). Operations: push, pop, find, reverse, length. |
|
| 5 | 5 | ||
| 6 | 6 | /// A node in the linked list. |
|
| 7 | - | record Node { |
|
| 7 | + | record Node: Copy { |
|
| 8 | 8 | value: i32, |
|
| 9 | 9 | next: ?u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// A linked list backed by a pool of pre-allocated nodes. |
|
| 13 | - | record List { |
|
| 13 | + | record List: Copy { |
|
| 14 | 14 | pool: [Node; 64], |
|
| 15 | 15 | free: u32, |
|
| 16 | 16 | head: ?u32, |
|
| 17 | 17 | } |
|
| 18 | 18 |
test/tests/prog.lzw.rad
+2 -2
| 6 | 6 | constant MAX_DICT: u32 = 512; |
|
| 7 | 7 | constant INIT_DICT: u32 = 258; |
|
| 8 | 8 | constant CLEAR_CODE: u32 = 256; |
|
| 9 | 9 | constant EOI_CODE: u32 = 257; |
|
| 10 | 10 | ||
| 11 | - | record DictEntry { |
|
| 11 | + | record DictEntry: Copy { |
|
| 12 | 12 | prefix: u32, |
|
| 13 | 13 | suffix: u8, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | record LzwState { |
|
| 16 | + | record LzwState: Copy { |
|
| 17 | 17 | encDict: *mut [DictEntry], |
|
| 18 | 18 | encDictSize: u32, |
|
| 19 | 19 | decDict: *mut [DictEntry], |
|
| 20 | 20 | decDictSize: u32, |
|
| 21 | 21 | encoded: *mut [u32], |
test/tests/prog.matmul.rad
+1 -1
| 4 | 4 | //! a known expected output. Classic benchmark kernel. |
|
| 5 | 5 | ||
| 6 | 6 | constant N: u32 = 4; |
|
| 7 | 7 | ||
| 8 | 8 | /// A 4x4 integer matrix (row-major). |
|
| 9 | - | record Mat4 { |
|
| 9 | + | record Mat4: Copy { |
|
| 10 | 10 | rows: [[i32; 4]; 4], |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Perform matrix multiplication: c = a * b. |
|
| 14 | 14 | fn matmul(c: *mut Mat4, a: *Mat4, b: *Mat4) { |
test/tests/prog.mersenne.rad
+1 -1
| 5 | 5 | constant M: u32 = 397; |
|
| 6 | 6 | constant MATRIX_A: u32 = 0x9908B0DF; |
|
| 7 | 7 | constant UPPER_MASK: u32 = 0x80000000; |
|
| 8 | 8 | constant LOWER_MASK: u32 = 0x7FFFFFFF; |
|
| 9 | 9 | ||
| 10 | - | record MtState { |
|
| 10 | + | record MtState: Copy { |
|
| 11 | 11 | mt: *mut [u32], |
|
| 12 | 12 | mti: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn mtInit(s: *mut MtState, seed: u32) { |
test/tests/prog.nqueens.rad
+1 -1
| 3 | 3 | //! Solve the N-Queens problem using backtracking. Count all valid placements |
|
| 4 | 4 | //! for boards of size 1 through 8 and verify against known solution counts. |
|
| 5 | 5 | ||
| 6 | 6 | constant MAX_N: u32 = 8; |
|
| 7 | 7 | ||
| 8 | - | record Board { |
|
| 8 | + | record Board: Copy { |
|
| 9 | 9 | queens: *mut [i32], |
|
| 10 | 10 | solutionCount: u32, |
|
| 11 | 11 | boardSize: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 |
test/tests/prog.rbtree.rad
+2 -2
| 6 | 6 | constant NIL: u32 = 0; |
|
| 7 | 7 | ||
| 8 | 8 | constant RED: u32 = 0; |
|
| 9 | 9 | constant BLACK: u32 = 1; |
|
| 10 | 10 | ||
| 11 | - | record RBNode { |
|
| 11 | + | record RBNode: Copy { |
|
| 12 | 12 | key: i32, |
|
| 13 | 13 | color: u32, |
|
| 14 | 14 | left: u32, |
|
| 15 | 15 | right: u32, |
|
| 16 | 16 | parent: u32, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | record RBTree { |
|
| 19 | + | record RBTree: Copy { |
|
| 20 | 20 | pool: *mut [RBNode], |
|
| 21 | 21 | poolNext: u32, |
|
| 22 | 22 | root: u32, |
|
| 23 | 23 | inorder: *mut [i32], |
|
| 24 | 24 | inorderCount: u32, |
test/tests/prog.regex.rad
+3 -3
| 10 | 10 | ||
| 11 | 11 | constant TRANS_CHAR: u32 = 0; |
|
| 12 | 12 | constant TRANS_EPSILON: u32 = 1; |
|
| 13 | 13 | constant TRANS_DOT: u32 = 2; |
|
| 14 | 14 | ||
| 15 | - | record Trans { |
|
| 15 | + | record Trans: Copy { |
|
| 16 | 16 | kind: u32, |
|
| 17 | 17 | ch: u8, |
|
| 18 | 18 | to: u32, |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | record Frag { |
|
| 21 | + | record Frag: Copy { |
|
| 22 | 22 | start: u32, |
|
| 23 | 23 | endState: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | - | record NfaState { |
|
| 26 | + | record NfaState: Copy { |
|
| 27 | 27 | trans: *mut [Trans], |
|
| 28 | 28 | transCount: u32, |
|
| 29 | 29 | stateFirst: *mut [u32], |
|
| 30 | 30 | transNext: *mut [u32], |
|
| 31 | 31 | stateCount: u32, |
test/tests/prog.sha256.rad
+1 -1
| 2 | 2 | //! SHA-256 (single block). |
|
| 3 | 3 | //! Implement the SHA-256 compression function for a single 512-bit block. |
|
| 4 | 4 | //! Verify against a known hash of a short message. |
|
| 5 | 5 | ||
| 6 | 6 | /// SHA-256 mutable state: hash values and message schedule. |
|
| 7 | - | record Sha256 { |
|
| 7 | + | record Sha256: Copy { |
|
| 8 | 8 | h: [u32; 8], |
|
| 9 | 9 | w: [u32; 64], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// SHA-256 initial hash values (first 32 bits of fractional parts of square |
test/tests/prog.symtab.rad
+3 -3
| 9 | 9 | constant MAX_SCOPES: u32 = 16; |
|
| 10 | 10 | constant HASH_SIZE: u32 = 64; |
|
| 11 | 11 | constant NIL: u32 = 0xFFFFFFFF; |
|
| 12 | 12 | ||
| 13 | 13 | /// A symbol entry in the table. |
|
| 14 | - | record Symbol { |
|
| 14 | + | record Symbol: Copy { |
|
| 15 | 15 | /// Name of the symbol (hash for comparison). |
|
| 16 | 16 | nameHash: u32, |
|
| 17 | 17 | /// The value associated with this symbol. |
|
| 18 | 18 | value: i32, |
|
| 19 | 19 | /// Scope depth at which this symbol was defined. |
| 23 | 23 | /// Previous symbol with the same name (for shadowing). |
|
| 24 | 24 | shadow: u32, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// A scope boundary marker. |
|
| 28 | - | record ScopeMarker { |
|
| 28 | + | record ScopeMarker: Copy { |
|
| 29 | 29 | /// Number of symbols when scope was entered. |
|
| 30 | 30 | symbolCount: u32, |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// The symbol table. |
|
| 34 | - | record SymTab { |
|
| 34 | + | record SymTab: Copy { |
|
| 35 | 35 | symbols: *mut [Symbol], |
|
| 36 | 36 | symbolCount: u32, |
|
| 37 | 37 | scopes: *mut [ScopeMarker], |
|
| 38 | 38 | scopeDepth: u32, |
|
| 39 | 39 | buckets: *mut [u32], |
test/tests/prog.tokenizer.rad
+7 -7
| 4 | 4 | //! then parses and evaluates them. Exercises: tagged unions with payloads, |
|
| 5 | 5 | //! match statements, optionals, if-let, while-let, for-in loops, records |
|
| 6 | 6 | //! with slice fields, and complex control flow. |
|
| 7 | 7 | ||
| 8 | 8 | /// Token types produced by the lexer. |
|
| 9 | - | union Token { |
|
| 9 | + | union Token: Copy { |
|
| 10 | 10 | /// Integer literal with its value. |
|
| 11 | 11 | Number(i32), |
|
| 12 | 12 | /// + |
|
| 13 | 13 | Plus, |
|
| 14 | 14 | /// - |
| 26 | 26 | /// Invalid character. |
|
| 27 | 27 | Invalid(u8), |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// A lexer state over a byte slice. |
|
| 31 | - | record Lexer { |
|
| 31 | + | record Lexer: Copy { |
|
| 32 | 32 | source: *[u8], |
|
| 33 | 33 | pos: u32, |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | /// A list of tokens with a fixed-size backing store. |
|
| 37 | - | record TokenList { |
|
| 37 | + | record TokenList: Copy { |
|
| 38 | 38 | tokens: *mut [Token], |
|
| 39 | 39 | count: u32, |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// AST node for parsed expressions. |
|
| 43 | - | union Expr { |
|
| 43 | + | union Expr: Copy { |
|
| 44 | 44 | Num(i32), |
|
| 45 | 45 | BinOp(BinOpData), |
|
| 46 | 46 | Neg(u32), |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Binary operation data. |
|
| 50 | - | record BinOpData { |
|
| 50 | + | record BinOpData: Copy { |
|
| 51 | 51 | op: u8, |
|
| 52 | 52 | left: u32, |
|
| 53 | 53 | right: u32, |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Pool of AST nodes. |
|
| 57 | - | record ExprPool { |
|
| 57 | + | record ExprPool: Copy { |
|
| 58 | 58 | nodes: *mut [Expr], |
|
| 59 | 59 | count: u32, |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | 62 | /// Parser state. |
|
| 63 | - | record Parser { |
|
| 63 | + | record Parser: Copy { |
|
| 64 | 64 | tokens: *[Token], |
|
| 65 | 65 | tokenCount: u32, |
|
| 66 | 66 | pos: u32, |
|
| 67 | 67 | pool: *mut ExprPool, |
|
| 68 | 68 | } |
test/tests/prog.vm.rad
+4 -4
| 9 | 9 | constant MAX_CODE: u32 = 256; |
|
| 10 | 10 | constant MAX_LOCALS: u32 = 16; |
|
| 11 | 11 | constant MAX_FRAMES: u32 = 8; |
|
| 12 | 12 | ||
| 13 | 13 | /// Bytecode instructions. |
|
| 14 | - | union Op { |
|
| 14 | + | union Op: Copy { |
|
| 15 | 15 | /// Push an immediate value. |
|
| 16 | 16 | Push(i32), |
|
| 17 | 17 | /// Pop top two, push sum. |
|
| 18 | 18 | Add, |
|
| 19 | 19 | /// Pop top two, push difference (second - first). |
| 49 | 49 | /// Halt execution, top of stack is result. |
|
| 50 | 50 | Halt, |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// A call frame for function calls. |
|
| 54 | - | record Frame { |
|
| 54 | + | record Frame: Copy { |
|
| 55 | 55 | returnAddr: u32, |
|
| 56 | 56 | localBase: u32, |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// The VM state. |
|
| 60 | - | record VM { |
|
| 60 | + | record VM: Copy { |
|
| 61 | 61 | code: *[Op], |
|
| 62 | 62 | codeLen: u32, |
|
| 63 | 63 | stack: *mut [i32], |
|
| 64 | 64 | sp: u32, |
|
| 65 | 65 | locals: *mut [i32], |
| 67 | 67 | frameCount: u32, |
|
| 68 | 68 | pc: u32, |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// VM error types. |
|
| 72 | - | union VmError { |
|
| 72 | + | union VmError: Copy { |
|
| 73 | 73 | /// Stack underflow error. |
|
| 74 | 74 | StackUnderflow, |
|
| 75 | 75 | /// Stack overflow error. |
|
| 76 | 76 | StackOverflow, |
|
| 77 | 77 | /// Division by zero. |
test/tests/ptr.addressof.field.rad
+1 -1
| 1 | - | record Point { x: i32, y: i32 } |
|
| 1 | + | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Takes the address of a record field. |
|
| 4 | 4 | fn addressOfField(p: *Point) -> *i32 { |
|
| 5 | 5 | return &p.y; |
|
| 6 | 6 | } |
test/tests/ptr.addressof.local.rad
+1 -1
| 1 | - | record Point { x: i32, y: i32 } |
|
| 1 | + | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Address of a record local (aggregate) - value is already a pointer. |
|
| 4 | 4 | fn addressOfRecordLocal() -> *Point { |
|
| 5 | 5 | let p = Point { x: 1, y: 2 }; |
|
| 6 | 6 | return &p; |
test/tests/ptr.deref.record.rad
+1 -1
| 1 | - | record Point { x: i32, y: i32 } |
|
| 1 | + | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Dereferences a record pointer and accesses a field. |
|
| 4 | 4 | fn derefField(p: *Point) -> i32 { |
|
| 5 | 5 | return (*p).x; |
|
| 6 | 6 | } |
test/tests/ptr.eq.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer equality uses address comparison, not value comparison. |
test/tests/record.access.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Vector { |
|
| 3 | + | record Vector: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | z: i32, |
|
| 7 | 7 | w: i32, |
|
| 8 | 8 | } |
test/tests/record.alignment.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test record alignment computation. |
|
| 3 | 3 | ||
| 4 | - | record R { |
|
| 4 | + | record R: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | @default fn main() -> i32 { |
test/tests/record.array.elements.rad
+2 -2
| 1 | 1 | //! returns: 115 |
|
| 2 | 2 | // Testing structs with array elements. |
|
| 3 | 3 | ||
| 4 | - | record Vector { |
|
| 4 | + | record Vector: Copy { |
|
| 5 | 5 | data: [i32; 4], |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Matrix { |
|
| 8 | + | record Matrix: Copy { |
|
| 9 | 9 | rows: [Vector; 3], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | fn vectorSum(vec: Vector) -> i32 { |
|
| 13 | 13 | let mut sum: i32 = 0; |
test/tests/record.assign.blit.rad
+1 -1
| 1 | - | record Point { x: i32, y: i32 } |
|
| 1 | + | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | fn copy() { |
|
| 4 | 4 | let p = Point { x: 1, y: 2 }; |
|
| 5 | 5 | let mut q = p; |
|
| 6 | 6 | set q = p; |
test/tests/record.copy.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test record value semantics through copying, mutation, and function calls. |
|
| 3 | - | record S { |
|
| 3 | + | record S: Copy { |
|
| 4 | 4 | x: i16, |
|
| 5 | 5 | y: i16, |
|
| 6 | 6 | z: i16, |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/record.ctor.tuple.rad
+1 -1
| 1 | - | record Pair(i32, i32); |
|
| 1 | + | record Pair: Copy(i32, i32); |
|
| 2 | 2 | ||
| 3 | 3 | fn make() -> Pair { |
|
| 4 | 4 | return Pair(1, 2); |
|
| 5 | 5 | } |
test/tests/record.empty.eq.rad
+1 -1
| 1 | 1 | // Test empty record equality |
|
| 2 | 2 | ||
| 3 | - | record Empty {} |
|
| 3 | + | record Empty: Copy {} |
|
| 4 | 4 | ||
| 5 | 5 | fn emptyEq(a: Empty, b: Empty) -> bool { |
|
| 6 | 6 | return a == b; |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/record.eq.rad
+1 -1
| 1 | 1 | // Holds two integer fields for equality comparisons. |
|
| 2 | - | record Pair { |
|
| 2 | + | record Pair: Copy { |
|
| 3 | 3 | left: i32, |
|
| 4 | 4 | right: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | // Returns true when the two records have identical fields. |
test/tests/record.field.assign.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Vector { x: i32, y: i32 } |
|
| 3 | + | record Vector: Copy { x: i32, y: i32 } |
|
| 4 | 4 | ||
| 5 | 5 | @default fn main() -> i32 { |
|
| 6 | 6 | let mut v: Vector = Vector { x: 3, y: 2 }; |
|
| 7 | 7 | ||
| 8 | 8 | set v.x = 40; |
test/tests/record.literal.labeled.rad
+1 -1
| 1 | - | record Point { x: i32, y: i32 } |
|
| 1 | + | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | fn get() -> i32 { |
|
| 4 | 4 | let p = Point { x: 1, y: 2 }; |
|
| 5 | 5 | return p.x; |
|
| 6 | 6 | } |
test/tests/record.mixed.layout.rad
+1 -1
| 1 | - | record Mixed { a: i8, b: i32, c: i16 } |
|
| 1 | + | record Mixed: Copy { a: i8, b: i32, c: i16 } |
|
| 2 | 2 | ||
| 3 | 3 | fn get() -> i16 { |
|
| 4 | 4 | let m = Mixed { a: 1, b: 2, c: 3 }; |
|
| 5 | 5 | return m.c; |
|
| 6 | 6 | } |
test/tests/record.nested.calls.2.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test nested record function calls with scaling and addition. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn add(p1: Point, p2: Point) -> Point { |
test/tests/record.nested.calls.3.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test record passing through multiple nested function calls. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn id(x: i32) -> i32 { |
test/tests/record.nested.eq.rad
+2 -2
| 1 | 1 | // Test nested aggregate equality |
|
| 2 | 2 | ||
| 3 | - | record Inner { |
|
| 3 | + | record Inner: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | record Outer { |
|
| 8 | + | record Outer: Copy { |
|
| 9 | 9 | a: Inner, |
|
| 10 | 10 | b: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn innerEq(a: Inner, b: Inner) -> bool { |
test/tests/record.nested.lit.rad
+2 -2
| 1 | - | record Inner { a: i32, b: i32 } |
|
| 2 | - | record Outer { inner: Inner, c: i32 } |
|
| 1 | + | record Inner: Copy { a: i32, b: i32 } |
|
| 2 | + | record Outer: Copy { inner: Inner, c: i32 } |
|
| 3 | 3 | ||
| 4 | 4 | fn get() -> i32 { |
|
| 5 | 5 | let o = Outer { inner: Inner { a: 1, b: 2 }, c: 3 }; |
|
| 6 | 6 | return o.inner.b; |
|
| 7 | 7 | } |
test/tests/record.param.lit.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test passing record literals directly into a function. |
|
| 3 | 3 | ||
| 4 | - | record Vector { |
|
| 4 | + | record Vector: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | z: i32 |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Point { |
|
| 10 | + | record Point: Copy { |
|
| 11 | 11 | x: i32, |
|
| 12 | 12 | y: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn compute(v: Vector, p: Point) -> i32 { |
test/tests/record.ptr.access.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | @default fn main() -> i32 { |
test/tests/record.ptr.mutate.rad
+1 -1
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | record Point { |
|
| 3 | + | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn mutate(p: *mut Point) { |
test/tests/record.shorthand.rad
+2 -2
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | //! Test shorthand field syntax in record literals and patterns. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | union Shape { |
|
| 9 | + | union Shape: Copy { |
|
| 10 | 10 | circle(i32), |
|
| 11 | 11 | rectangle { width: i32, height: i32 }, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Test shorthand record literal construction. |
test/tests/record.unlabeled.deref.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test deref behavior for single-field unlabeled records. |
|
| 3 | 3 | ||
| 4 | - | record Wrap(i32); |
|
| 5 | - | record WrapBool(bool); |
|
| 6 | - | record WrapU8(u8); |
|
| 7 | - | record WrapU64(u64); |
|
| 4 | + | record Wrap: Copy(i32); |
|
| 5 | + | record WrapBool: Copy(bool); |
|
| 6 | + | record WrapU8: Copy(u8); |
|
| 7 | + | record WrapU64: Copy(u64); |
|
| 8 | 8 | ||
| 9 | 9 | fn getInner(w: Wrap) -> i32 { |
|
| 10 | 10 | return *w; |
|
| 11 | 11 | } |
|
| 12 | 12 |
test/tests/record.unlabeled.rad
+2 -2
| 1 | 1 | //! returns: 1 |
|
| 2 | 2 | // TODO: Test destructuring. |
|
| 3 | 3 | ||
| 4 | - | record S(i32); |
|
| 5 | - | record R(bool, u8); |
|
| 4 | + | record S: Copy(i32); |
|
| 5 | + | record R: Copy(bool, u8); |
|
| 6 | 6 | ||
| 7 | 7 | fn passS(s: S) -> S { |
|
| 8 | 8 | return s; |
|
| 9 | 9 | } |
|
| 10 | 10 |
test/tests/reserve.loop.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | - | record Pair { a: i32, b: i32 } |
|
| 2 | + | record Pair: Copy { a: i32, b: i32 } |
|
| 3 | 3 | ||
| 4 | 4 | @default fn main() -> i32 { |
|
| 5 | 5 | let mut total: i32 = 0; |
|
| 6 | 6 | let mut i: i32 = 0; |
|
| 7 | 7 | while i < 10000 { |
test/tests/result.void.success.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that no-result success returns work correctly. |
|
| 3 | 3 | ||
| 4 | - | union TestError { |
|
| 4 | + | union TestError: Copy { |
|
| 5 | 5 | Failed, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn voidSuccess() throws (TestError) { |
|
| 9 | 9 | return; |
test/tests/set.keyword.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test the `set` keyword for assignment statements. |
|
| 3 | 3 | ||
| 4 | - | record Point { |
|
| 4 | + | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | @default fn main() -> i32 { |
test/tests/slice.append.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice .append() method with inline allocator. |
|
| 3 | 3 | ||
| 4 | 4 | /// Simple bump allocator for testing. |
|
| 5 | - | record Arena { |
|
| 5 | + | record Arena: Copy { |
|
| 6 | 6 | data: *mut [u8], |
|
| 7 | 7 | offset: u32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | fn newArena(data: *mut [u8]) -> Arena { |
| 22 | 22 | ||
| 23 | 23 | return base as *mut opaque; |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Allocator record matching the compiler's expected layout. |
|
| 27 | - | record Allocator { |
|
| 27 | + | record Allocator: Copy { |
|
| 28 | 28 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 29 | 29 | ctx: *mut opaque, |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque { |
test/tests/spill.blockarg.clobber.rad
+1 -1
| 31 | 31 | ||
| 32 | 32 | fn ssig1(x: u32) -> u32 { |
|
| 33 | 33 | return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | record State { |
|
| 36 | + | record State: Copy { |
|
| 37 | 37 | h: [u32; 8], |
|
| 38 | 38 | w: [u32; 64], |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | fn prepareSchedule(s: *mut State, block: *[u32]) { |
test/tests/spill.loop.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test register spilling in a loop with many live values. |
|
| 3 | 3 | //! Computes record layout from field tags, exercising alignment and offset |
|
| 4 | 4 | //! calculations that require multiple simultaneously live registers. |
|
| 5 | 5 | ||
| 6 | - | record Layout { |
|
| 6 | + | record Layout: Copy { |
|
| 7 | 7 | size: u32, |
|
| 8 | 8 | alignment: u32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn getLayout(tag: u8) -> Layout { |
test/tests/static.record.array.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test mutating a static record containing an array. |
|
| 3 | 3 | ||
| 4 | - | record Record { |
|
| 4 | + | record Record: Copy { |
|
| 5 | 5 | values: [i32; 4], |
|
| 6 | 6 | bias: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | static TABLE: Record = Record { |
test/tests/static.slice.index.assign.rad
+2 -2
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | record Inner { |
|
| 3 | + | record Inner: Copy { |
|
| 4 | 4 | value: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | record Container { |
|
| 7 | + | record Container: Copy { |
|
| 8 | 8 | pad: i32, |
|
| 9 | 9 | items: [Inner; 3], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | static container: Container = undefined; |
test/tests/static.slice.offset.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice indexing into a static table with offsets. |
|
| 3 | 3 | ||
| 4 | - | record Entry { |
|
| 4 | + | record Entry: Copy { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record Table { |
|
| 9 | + | record Table: Copy { |
|
| 10 | 10 | scratch: *mut [Entry], |
|
| 11 | 11 | entries: *mut [Entry], |
|
| 12 | 12 | len: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 |
test/tests/static.zero.bss.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! rw-data-size: 0 |
|
| 3 | 3 | ||
| 4 | 4 | /// Static pool used to verify that partially initialized zero data is BSS. |
|
| 5 | - | record Pool { |
|
| 5 | + | record Pool: Copy { |
|
| 6 | 6 | /// Undefined slice table. |
|
| 7 | 7 | table: [*[u8]; 32], |
|
| 8 | 8 | /// Explicitly initialized count. |
|
| 9 | 9 | count: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Padded record used to verify that zero fields plus undefined padding are BSS. |
|
| 13 | - | record Padded { |
|
| 13 | + | record Padded: Copy { |
|
| 14 | 14 | /// Leading byte. |
|
| 15 | 15 | tag: u8, |
|
| 16 | 16 | /// Field that forces padding after `tag`. |
|
| 17 | 17 | value: u32, |
|
| 18 | 18 | } |
test/tests/switch.blockargs.clobber.rad
+1 -1
| 9 | 9 | //! In the isel, `emitBlockArgs` for case 0 writes `1` (the merge arg) to the |
|
| 10 | 10 | //! merge block's parameter register. If the register allocator assigned that |
|
| 11 | 11 | //! parameter register to the same physical register as the switch value, the |
|
| 12 | 12 | //! tag is clobbered before subsequent case comparisons run. |
|
| 13 | 13 | ||
| 14 | - | union Val { |
|
| 14 | + | union Val: Copy { |
|
| 15 | 15 | none, |
|
| 16 | 16 | empty, |
|
| 17 | 17 | data(i32), |
|
| 18 | 18 | } |
|
| 19 | 19 |
test/tests/trait.aggregate.ret.rad
+3 -3
| 2 | 2 | //! Test trait methods returning aggregate types. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: trait methods that return structs (both small and larger |
|
| 5 | 5 | //! than pointer size) and optionals via v-table dispatch. |
|
| 6 | 6 | ||
| 7 | - | record Point { |
|
| 7 | + | record Point: Copy { |
|
| 8 | 8 | x: i32, |
|
| 9 | 9 | y: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Vec3 { |
|
| 12 | + | record Vec3: Copy { |
|
| 13 | 13 | x: i32, |
|
| 14 | 14 | y: i32, |
|
| 15 | 15 | z: i32, |
|
| 16 | 16 | } |
|
| 17 | 17 |
| 19 | 19 | fn (*Geometry) origin() -> Point; |
|
| 20 | 20 | fn (*Geometry) center() -> Vec3; |
|
| 21 | 21 | fn (*Geometry) maybe() -> ?i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | record Circle { |
|
| 24 | + | record Circle: Copy { |
|
| 25 | 25 | cx: i32, |
|
| 26 | 26 | cy: i32, |
|
| 27 | 27 | radius: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 |
test/tests/trait.array.optional.rad
+2 -2
| 2 | 2 | //! Test trait objects stored in arrays and used as optional values. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: arrays of trait objects dispatched in loops, |
|
| 5 | 5 | //! and optional trait object values. |
|
| 6 | 6 | ||
| 7 | - | record Adder { |
|
| 7 | + | record Adder: Copy { |
|
| 8 | 8 | n: i32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | record Multiplier { |
|
| 11 | + | record Multiplier: Copy { |
|
| 12 | 12 | n: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Transform { |
|
| 16 | 16 | fn (*Transform) apply(x: i32) -> i32; |
test/tests/trait.basic.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Basic trait object dispatch test. |
|
| 3 | - | record Counter { |
|
| 3 | + | record Counter: Copy { |
|
| 4 | 4 | value: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | trait Adder { |
|
| 8 | 8 | fn (*mut Adder) add(n: i32) -> i32; |
test/tests/trait.control.flow.rad
+1 -1
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: trait method calls inside loops and conditionals, |
|
| 5 | 5 | //! verifying that vtable dispatch works correctly when interleaved |
|
| 6 | 6 | //! with branching and iteration. |
|
| 7 | 7 | ||
| 8 | - | record Counter { |
|
| 8 | + | record Counter: Copy { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Stepper { |
|
| 13 | 13 | fn (*mut Stepper) step() -> i32; |
test/tests/trait.dispatch.rad
+1 -1
| 1 | - | record Acc { |
|
| 1 | + | record Acc: Copy { |
|
| 2 | 2 | n: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Ops { |
|
| 6 | 6 | fn (*Ops) get() -> i32; |
test/tests/trait.fn.param.rad
+2 -2
| 2 | 2 | //! Test passing trait objects as function parameters. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: trait objects passed to non-method functions, including |
|
| 5 | 5 | //! both mutable and immutable trait object pointers as arguments. |
|
| 6 | 6 | ||
| 7 | - | record Circle { |
|
| 7 | + | record Circle: Copy { |
|
| 8 | 8 | radius: i32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | record Square { |
|
| 11 | + | record Square: Copy { |
|
| 12 | 12 | side: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Shape { |
|
| 16 | 16 | fn (*Shape) area() -> i32; |
test/tests/trait.multiple.methods.rad
+1 -1
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: a trait declaring multiple methods with void, bool, and |
|
| 5 | 5 | //! integer return types. Verifies vtable layout with more than one |
|
| 6 | 6 | //! entry and correct dispatch for each method. |
|
| 7 | 7 | ||
| 8 | - | record Accumulator { |
|
| 8 | + | record Accumulator: Copy { |
|
| 9 | 9 | total: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Collector { |
|
| 13 | 13 | fn (*mut Collector) add(n: i32) -> i32; |
test/tests/trait.multiple.traits.rad
+1 -1
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: a single concrete type that provides instances for |
|
| 5 | 5 | //! two different traits. Each trait object dispatches independently |
|
| 6 | 6 | //! through its own vtable. |
|
| 7 | 7 | ||
| 8 | - | record Counter { |
|
| 8 | + | record Counter: Copy { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Incrementable { |
|
| 13 | 13 | fn (*mut Incrementable) inc() -> i32; |
test/tests/trait.multiple.types.rad
+2 -2
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: two different concrete types implementing the same trait, |
|
| 5 | 5 | //! coerced to the same trait object type. Also tests immutable `*Trait` |
|
| 6 | 6 | //! receivers. Verifies each type dispatches through its own vtable. |
|
| 7 | 7 | ||
| 8 | - | record Dog { |
|
| 8 | + | record Dog: Copy { |
|
| 9 | 9 | age: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Cat { |
|
| 12 | + | record Cat: Copy { |
|
| 13 | 13 | lives: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | trait Speaker { |
|
| 17 | 17 | fn (*Speaker) speak() -> i32; |
test/tests/trait.object.rad
+1 -1
| 1 | - | record Counter { |
|
| 1 | + | record Counter: Copy { |
|
| 2 | 2 | value: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Adder { |
|
| 6 | 6 | fn (*mut Adder) add(n: i32) -> i32; |
test/tests/trait.supertrait.forward.rad
+1 -1
| 7 | 7 | ||
| 8 | 8 | trait Parent { |
|
| 9 | 9 | fn (*Parent) parent() -> i32; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | record Value { |
|
| 12 | + | record Value: Copy { |
|
| 13 | 13 | n: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | instance Parent for Value { |
|
| 17 | 17 | fn (self: *Value) parent() -> i32 { |
test/tests/trait.supertrait.rad
+1 -1
| 18 | 18 | ||
| 19 | 19 | trait ReadWriter: Reader + Writer { |
|
| 20 | 20 | fn (*mut ReadWriter) flush() -> i32; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | record Socket { |
|
| 23 | + | record Socket: Copy { |
|
| 24 | 24 | rbuf: [u8; 32], |
|
| 25 | 25 | rpos: i32, |
|
| 26 | 26 | rlen: i32, |
|
| 27 | 27 | wbuf: [u8; 32], |
|
| 28 | 28 | wpos: i32, |
test/tests/trait.throws.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test trait methods with throws clauses. |
|
| 3 | 3 | ||
| 4 | - | union ParseError { |
|
| 4 | + | union ParseError: Copy { |
|
| 5 | 5 | InvalidInput, |
|
| 6 | 6 | Overflow, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | record StrictParser { |
|
| 9 | + | record StrictParser: Copy { |
|
| 10 | 10 | limit: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | trait Parser { |
|
| 14 | 14 | fn (*Parser) parse(n: i32) -> i32 throws (ParseError); |
test/tests/trait.writer.rad
+2 -2
| 10 | 10 | fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 11 | 11 | fn (*Writer) total() -> i32; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Writes bytes into a fixed-size buffer. |
|
| 15 | - | record BufferWriter { |
|
| 15 | + | record BufferWriter: Copy { |
|
| 16 | 16 | buf: [u8; 64], |
|
| 17 | 17 | pos: i32, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | instance Writer for BufferWriter { |
| 35 | 35 | return w.pos; |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Counts bytes written through it, forwarding to an inner writer. |
|
| 40 | - | record CountingWriter { |
|
| 40 | + | record CountingWriter: Copy { |
|
| 41 | 41 | inner: *mut opaque Writer, |
|
| 42 | 42 | count: i32, |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | instance Writer for CountingWriter { |
test/tests/try.basic.rad
+1 -1
| 1 | 1 | /// Error type for fallible functions. |
|
| 2 | - | union Error { Boom } |
|
| 2 | + | union Error: Copy { Boom } |
|
| 3 | 3 | ||
| 4 | 4 | /// Return a value or throw an error based on the flag. |
|
| 5 | 5 | fn fallible(flag: bool) -> i32 throws (Error) { |
|
| 6 | 6 | if flag { |
|
| 7 | 7 | throw Error::Boom(); |
test/tests/try.catch.rad
+1 -1
| 1 | 1 | /// Ensure `try` with catch block diverges from the error path. |
|
| 2 | - | union Error { Boom } |
|
| 2 | + | union Error: Copy { Boom } |
|
| 3 | 3 | ||
| 4 | 4 | fn fallible(flag: bool) -> i32 throws (Error) { |
|
| 5 | 5 | if flag { |
|
| 6 | 6 | throw Error::Boom(); |
|
| 7 | 7 | } |
test/tests/try.optional.rad
+1 -1
| 1 | 1 | /// Error type for fallible functions. |
|
| 2 | - | union Error { Boom } |
|
| 2 | + | union Error: Copy { Boom } |
|
| 3 | 3 | ||
| 4 | 4 | /// Return a value or throw an error based on the flag. |
|
| 5 | 5 | fn fallible(flag: bool) -> i32 throws (Error) { |
|
| 6 | 6 | if flag { |
|
| 7 | 7 | throw Error::Boom(); |
test/tests/try.panic.rad
+1 -1
| 1 | 1 | /// Error type for fallible functions. |
|
| 2 | - | union Error { Boom } |
|
| 2 | + | union Error: Copy { Boom } |
|
| 3 | 3 | ||
| 4 | 4 | /// Return a value or throw an error based on the flag. |
|
| 5 | 5 | fn fallible(flag: bool) -> i32 throws (Error) { |
|
| 6 | 6 | if flag { |
|
| 7 | 7 | throw Error::Boom(); |
test/tests/undefined.aggregate.rad
+1 -1
| 1 | 1 | /// Test lowering of undefined for aggregate types. |
|
| 2 | - | record Point { x: i32, y: i32 } |
|
| 2 | + | record Point: Copy { x: i32, y: i32 } |
|
| 3 | 3 | ||
| 4 | 4 | fn test() -> i32 { |
|
| 5 | 5 | let p: Point = undefined; |
|
| 6 | 6 | return p.x; |
|
| 7 | 7 | } |
test/tests/undefined.record.field.rad
+3 -3
| 8 | 8 | /// |
|
| 9 | 9 | /// The fix skips stores for `undefined` fields entirely, leaving the |
|
| 10 | 10 | /// memory uninitialised. Covers both named-field syntax (lowerRecordFields) |
|
| 11 | 11 | /// and positional constructor syntax (lowerRecordCtor). |
|
| 12 | 12 | ||
| 13 | - | record Small { |
|
| 13 | + | record Small: Copy { |
|
| 14 | 14 | x: i32, |
|
| 15 | 15 | y: i32, |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | record WithArray { |
|
| 18 | + | record WithArray: Copy { |
|
| 19 | 19 | data: [i32; 4], |
|
| 20 | 20 | len: i32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Construct a record with one field `undefined` (named-field syntax). |
| 38 | 38 | fn fullInit() -> i32 { |
|
| 39 | 39 | let s = Small { x: 10, y: 20 }; |
|
| 40 | 40 | return s.x + s.y; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | - | union Tagged { |
|
| 43 | + | union Tagged: Copy { |
|
| 44 | 44 | A { data: [i32; 4], tag: i32 }, |
|
| 45 | 45 | B, |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Union variant with record payload containing an `undefined` field. |
test/tests/union-tag.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that union variant tags are stored correctly for unions with many variants. |
|
| 3 | 3 | ||
| 4 | - | union BigUnion { |
|
| 4 | + | union BigUnion: Copy { |
|
| 5 | 5 | V0, |
|
| 6 | 6 | V1, |
|
| 7 | 7 | V2, |
|
| 8 | 8 | V3, |
|
| 9 | 9 | V4, |
test/tests/union.bitfield.rad
+1 -1
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | union Attribute { |
|
| 3 | + | union Attribute: Copy { |
|
| 4 | 4 | None = 0b0, |
|
| 5 | 5 | Export = 0b10, |
|
| 6 | 6 | Default = 0b100, |
|
| 7 | 7 | Extern = 0b1000, |
|
| 8 | 8 | Test = 0b10000, |
test/tests/union.ctor.rad
+2 -2
| 1 | 1 | /// Union with tag-only variants. |
|
| 2 | - | union Color { Red, Green, Blue } |
|
| 2 | + | union Color: Copy { Red, Green, Blue } |
|
| 3 | 3 | ||
| 4 | 4 | /// Union with and without payload variants. |
|
| 5 | - | union MaybeInt { None, Some(i32) } |
|
| 5 | + | union MaybeInt: Copy { None, Some(i32) } |
|
| 6 | 6 | ||
| 7 | 7 | /// Builds a tag-only union variant. |
|
| 8 | 8 | fn makeRed() -> Color { |
|
| 9 | 9 | return Color::Red; |
|
| 10 | 10 | } |
test/tests/union.discriminant.cast.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Casting a void-union variant to `i32` should produce its explicit tag value. |
|
| 3 | 3 | ||
| 4 | - | union U { |
|
| 4 | + | union U: Copy { |
|
| 5 | 5 | A = 7, |
|
| 6 | 6 | B = 42, |
|
| 7 | 7 | C = 255, |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/union.edge.case.2.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | union NodeValue { |
|
| 3 | + | union NodeValue: Copy { |
|
| 4 | 4 | Placeholder, |
|
| 5 | 5 | Nil, |
|
| 6 | 6 | Bool(bool), |
|
| 7 | 7 | } |
|
| 8 | 8 |
test/tests/union.edge.case.3.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test union match through pointer dereference. |
|
| 3 | 3 | ||
| 4 | - | union Node { |
|
| 4 | + | union Node: Copy { |
|
| 5 | 5 | Placeholder(u8), |
|
| 6 | 6 | Nil(u8), |
|
| 7 | 7 | Bool(bool), |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | record Parser { |
|
| 10 | + | record Parser: Copy { |
|
| 11 | 11 | root: Node, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | fn node(p: *mut Parser, value: Node) -> *Node { |
|
| 15 | 15 | set p.root = value; |
test/tests/union.eq.rad
+4 -4
| 1 | 1 | /// Void union - only tag comparison needed. |
|
| 2 | - | union Color { Red, Green, Blue } |
|
| 2 | + | union Color: Copy { Red, Green, Blue } |
|
| 3 | 3 | ||
| 4 | 4 | /// Union with one payload variant. |
|
| 5 | - | union MaybeInt { None, Some(i32) } |
|
| 5 | + | union MaybeInt: Copy { None, Some(i32) } |
|
| 6 | 6 | ||
| 7 | 7 | /// Union with multiple different payload types. |
|
| 8 | - | union Value { Empty, Int(i32), Byte(u8) } |
|
| 8 | + | union Value: Copy { Empty, Int(i32), Byte(u8) } |
|
| 9 | 9 | ||
| 10 | 10 | /// Union with multi-field record payload. |
|
| 11 | - | union Shape { None, Point { x: i32, y: i32 } } |
|
| 11 | + | union Shape: Copy { None, Point { x: i32, y: i32 } } |
|
| 12 | 12 | ||
| 13 | 13 | /// Compare void unions (tags only). |
|
| 14 | 14 | fn colorEq(a: Color, b: Color) -> bool { |
|
| 15 | 15 | return a == b; |
|
| 16 | 16 | } |
test/tests/union.eq.void.ctor.rad
+1 -1
| 1 | 1 | //! Regression test: comparing a void union variable against a constructor |
|
| 2 | 2 | //! literal must compare tag scalars directly, not load through a pointer. |
|
| 3 | 3 | ||
| 4 | - | union Op { Add, Sub, Mul } |
|
| 4 | + | union Op: Copy { Add, Sub, Mul } |
|
| 5 | 5 | ||
| 6 | 6 | /// Compare void union against constructor literal. |
|
| 7 | 7 | fn isAdd(op: Op) -> bool { |
|
| 8 | 8 | return op == Op::Add; |
|
| 9 | 9 | } |
16 more files not shown.