compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.9 KiB
gen.rad
513 B
il.rad
20.4 KiB
lower.rad
321.7 KiB
module.rad
17.3 KiB
package.rad
1.3 KiB
parser.rad
92.2 KiB
resolver.rad
511.1 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/lang/resolver.rad
raw
| 1 | //! Radiance semantic analyzer and type resolver. |
| 2 | //! |
| 3 | //! This module performs scope construction, symbol binding, and identifier |
| 4 | //! resolution on top of the AST produced by the parser. |
| 5 | |
| 6 | export mod printer; |
| 7 | |
| 8 | /// Unit tests for the resolver. |
| 9 | @test mod tests; |
| 10 | |
| 11 | /// Tests for module-owned record representations. |
| 12 | @test mod opaqueTests; |
| 13 | |
| 14 | // TODO: Move to raw vectors to reduce list duplication? |
| 15 | // TODO: When a function declaration fails to typecheck, it should still "exist". |
| 16 | // TODO: `ensureNominalResolved` should just run when you call `typeFor`. |
| 17 | // TODO: Have different types for positional vs. named field records. |
| 18 | |
| 19 | use std::mem; |
| 20 | use std::io; |
| 21 | use std::lang::alloc; |
| 22 | use std::lang::types; |
| 23 | use std::lang::ast; |
| 24 | use std::lang::parser; |
| 25 | use std::lang::module; |
| 26 | |
| 27 | /// Maximum number of diagnostics recorded. |
| 28 | export constant MAX_ERRORS: u32 = 64; |
| 29 | |
| 30 | /// Power-of-two bucket count for interned type lookup chains. |
| 31 | constant TYPE_BUCKETS: u32 = 1024; |
| 32 | /// Power-of-two bucket count for exact nominal application lookup chains. |
| 33 | constant APPLICATION_BUCKETS: u32 = 1024; |
| 34 | /// Odd multiplier that combines cache key components. |
| 35 | constant CACHE_HASH_PRIME: u32 = 16777619; |
| 36 | |
| 37 | /// Synthetic function name used when wrapping a bare expression for analysis. |
| 38 | export constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__"; |
| 39 | /// Synthetic function name used when wrapping a block for analysis. |
| 40 | export constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__"; |
| 41 | |
| 42 | /// Maximum number of symbols stored within a module scope. |
| 43 | export constant MAX_MODULE_SYMBOLS: u32 = 528; |
| 44 | /// Maximum number of symbols stored within a local scope. |
| 45 | export constant MAX_LOCAL_SYMBOLS: u32 = 32; |
| 46 | /// Maximum function parameters. |
| 47 | export constant MAX_FN_PARAMS: u32 = 8; |
| 48 | /// Maximum function thrown types. |
| 49 | export constant MAX_FN_THROWS: u32 = 8; |
| 50 | /// Maximum number of variants in a union. |
| 51 | /// Nb. This should not be raised above `255`, |
| 52 | /// as tags are stored using 8-bits only. |
| 53 | export constant MAX_UNION_VARIANTS: u32 = 128; |
| 54 | /// Maximum nesting of loops. |
| 55 | export constant MAX_LOOP_DEPTH: u32 = 16; |
| 56 | /// Maximum trait instances. |
| 57 | export constant MAX_INSTANCES: u32 = 128; |
| 58 | /// Maximum standalone methods (across all types). |
| 59 | export constant MAX_METHODS: u32 = 256; |
| 60 | /// Maximum number of linear bindings active in one function. |
| 61 | constant MAX_LINEAR_BINDINGS: u32 = 32; |
| 62 | /// Maximum full-region projections active in nested lexical regions. |
| 63 | constant MAX_REGIONAL_LOANS: u32 = 32; |
| 64 | /// Maximum inline field depth used to prove borrow separation. |
| 65 | constant MAX_BORROW_FIELDS: u32 = 16; |
| 66 | /// Maximum nesting depth tracked for loops. |
| 67 | constant MAX_LINEAR_LOOP_DEPTH: u32 = 16; |
| 68 | |
| 69 | /// Trait definition stored in the resolver. |
| 70 | export record TraitType: Copy { |
| 71 | /// Trait name. |
| 72 | name: *[u8], |
| 73 | /// Module that declares the trait. |
| 74 | moduleId: u16, |
| 75 | /// Method signatures, including from supertraits. |
| 76 | methods: *unsafe mut [TraitMethod], |
| 77 | /// Supertraits that must also be implemented. |
| 78 | supertraits: *unsafe mut [*unsafe TraitType], |
| 79 | } |
| 80 | |
| 81 | /// A single method signature within a trait. |
| 82 | export record TraitMethod: Copy { |
| 83 | /// Method name. |
| 84 | name: *[u8], |
| 85 | /// Function type for the method, excluding the receiver. |
| 86 | fnType: *FnType, |
| 87 | /// Whether the receiver is mutable. |
| 88 | mutable: bool, |
| 89 | /// Pointer-like class used by the receiver. |
| 90 | receiverClass: types::PointerClass, |
| 91 | /// V-table slot index. |
| 92 | index: u32, |
| 93 | } |
| 94 | |
| 95 | /// An entry in the trait instance registry. |
| 96 | export record InstanceEntry: Copy { |
| 97 | /// Trait type descriptor. |
| 98 | traitType: *unsafe TraitType, |
| 99 | /// Concrete type that implements the trait. |
| 100 | concreteType: Type, |
| 101 | /// Name of the concrete type. |
| 102 | concreteTypeName: *[u8], |
| 103 | /// Module where this instance was declared. |
| 104 | moduleId: u16, |
| 105 | /// Method symbols for each trait method, in declaration order. |
| 106 | methods: *unsafe mut [*unsafe mut Symbol], |
| 107 | } |
| 108 | |
| 109 | /// An entry in the method registry. |
| 110 | export record MethodEntry: Copy { |
| 111 | /// Module where the method is defined. |
| 112 | moduleId: u16, |
| 113 | /// Concrete type that owns the method. |
| 114 | concreteType: Type, |
| 115 | /// Name of the concrete type. |
| 116 | concreteTypeName: *[u8], |
| 117 | /// Method name. |
| 118 | name: *[u8], |
| 119 | /// Function type excluding the receiver. |
| 120 | fnType: *FnType, |
| 121 | /// Whether the receiver is mutable. |
| 122 | mutable: bool, |
| 123 | /// Pointer-like class used by the receiver. |
| 124 | receiverClass: types::PointerClass, |
| 125 | /// Resolver-local identity of the method symbol. |
| 126 | symbolId: u32, |
| 127 | /// Function type including the receiver, used for emitted calls. |
| 128 | fullFnType: *FnType, |
| 129 | } |
| 130 | |
| 131 | /// Identifier for the synthetic `len` field. |
| 132 | export constant LEN_FIELD: *[u8] = "len"; |
| 133 | /// Identifier for the synthetic `ptr` field. |
| 134 | export constant PTR_FIELD: *[u8] = "ptr"; |
| 135 | /// Identifier for the synthetic `cap` field. |
| 136 | export constant CAP_FIELD: *[u8] = "cap"; |
| 137 | |
| 138 | /// Maximum `u16` value. |
| 139 | constant U16_MAX: u16 = 0xFFFF; |
| 140 | /// Maximum `u8` value. |
| 141 | constant U8_MAX: u16 = 0xFF; |
| 142 | |
| 143 | /// Minimum `i8` value. |
| 144 | constant I8_MIN: i32 = -128; |
| 145 | /// Maximum `i8` value. |
| 146 | constant I8_MAX: i32 = 127; |
| 147 | /// Minimum `i16` value. |
| 148 | constant I16_MIN: i32 = -32768; |
| 149 | /// Maximum `i16` value. |
| 150 | constant I16_MAX: i32 = 32767; |
| 151 | |
| 152 | /// Minimum `i32` value. |
| 153 | constant I32_MIN: i32 = -2147483648; |
| 154 | /// Maximum `i32` value. |
| 155 | constant I32_MAX: i32 = 2147483647; |
| 156 | /// Minimum `i64` value: -(2^63). |
| 157 | constant I64_MIN: i64 = -9223372036854775808; |
| 158 | /// Maximum `i64` value: 2^63 - 1. |
| 159 | constant I64_MAX: i64 = 9223372036854775807; |
| 160 | |
| 161 | /// Size of a pointer in bytes. |
| 162 | export constant PTR_SIZE: u32 = 8; |
| 163 | |
| 164 | /// Information about a record or tuple field. |
| 165 | export record RecordField: Copy { |
| 166 | /// Field name, `nil` for positional fields. |
| 167 | name: ?*[u8], |
| 168 | /// Field type. |
| 169 | fieldType: Type, |
| 170 | /// Byte offset from the start of the record. |
| 171 | offset: i32, |
| 172 | } |
| 173 | |
| 174 | /// Information about a union variant. |
| 175 | record UnionVariant: Copy { |
| 176 | name: *[u8], |
| 177 | valueType: Type, |
| 178 | symbol: *unsafe mut Symbol, |
| 179 | } |
| 180 | |
| 181 | /// Array type payload. |
| 182 | export record ArrayType: Copy { |
| 183 | item: *Type, |
| 184 | length: u32, |
| 185 | } |
| 186 | |
| 187 | /// Record nominal type. |
| 188 | export record RecordType: Copy { |
| 189 | /// Module that can access the representation, or `nil` for public fields. |
| 190 | privateModule: ?u16, |
| 191 | /// Region parameters of the source declaration. |
| 192 | regions: ?*RegionScope, |
| 193 | /// Exact region arguments, if this is an applied type. |
| 194 | application: ?*unsafe mut NominalApplication, |
| 195 | fields: *unsafe [RecordField], |
| 196 | labeled: bool, |
| 197 | /// Shared layout of the source declaration. |
| 198 | layout: *Layout, |
| 199 | /// Whether the declaration explicitly carries the `Once` marker. |
| 200 | declaredLinear: bool, |
| 201 | /// Whether the declaration explicitly carries the `Copy` marker. |
| 202 | declaredCopy: bool, |
| 203 | } |
| 204 | |
| 205 | /// Union nominal type. |
| 206 | export record UnionType: Copy { |
| 207 | /// Region parameters of the source declaration. |
| 208 | regions: ?*RegionScope, |
| 209 | /// Exact region arguments, if this is an applied type. |
| 210 | application: ?*unsafe mut NominalApplication, |
| 211 | variants: *unsafe [UnionVariant], |
| 212 | /// Shared layout of the source declaration. |
| 213 | layout: *Layout, |
| 214 | /// Cached payload offset within the union aggregate. |
| 215 | valOffset: u32, |
| 216 | /// If all variants have void payloads. |
| 217 | isAllVoid: bool, |
| 218 | /// Whether the declaration explicitly carries the `Once` marker. |
| 219 | declaredLinear: bool, |
| 220 | /// Whether the declaration explicitly carries the `Copy` marker. |
| 221 | declaredCopy: bool, |
| 222 | } |
| 223 | |
| 224 | /// Metadata for user-defined types. |
| 225 | export union NominalType: Copy { |
| 226 | /// Placeholder for a type that hasn't been fully resolved yet. |
| 227 | /// Stores the declaration node for lazy resolution. |
| 228 | Placeholder(*ast::Node), |
| 229 | /// Declaration whose value layout is under analysis. |
| 230 | Resolving(*ast::Node), |
| 231 | /// Applied type whose field or variant view is not yet resolved. |
| 232 | Application(*unsafe mut NominalApplication), |
| 233 | Record(RecordType), |
| 234 | Union(UnionType), |
| 235 | } |
| 236 | |
| 237 | /// Coercion plan, when coercion from one type to another. |
| 238 | export union Coercion: Copy { |
| 239 | /// No coercion, eg. `T -> T`. |
| 240 | Identity, |
| 241 | /// Eg. `u8 -> i32`. Stores both source and target types for lowering. |
| 242 | NumericCast { from: Type, to: Type }, |
| 243 | /// Eg. `T -> ?T`. Stores the inner value type. |
| 244 | OptionalLift(Type), |
| 245 | /// Wrap return value in success variant of result type. |
| 246 | ResultWrap, |
| 247 | /// Coerce a concrete pointer to a trait object. |
| 248 | TraitObject { |
| 249 | /// Trait type information. |
| 250 | traitInfo: *unsafe TraitType, |
| 251 | /// Instance entry for v-table lookup. |
| 252 | inst: *unsafe InstanceEntry, |
| 253 | }, |
| 254 | } |
| 255 | |
| 256 | /// Result of resolving a module path. |
| 257 | record ResolvedModule: Copy { |
| 258 | /// Module entry in the graph. |
| 259 | entry: *module::ModuleEntry, |
| 260 | /// Scope containing the module's declarations. |
| 261 | scope: *unsafe mut Scope, |
| 262 | } |
| 263 | |
| 264 | /// Type layout. |
| 265 | export record Layout: Copy { |
| 266 | /// Size in bytes. |
| 267 | size: u32, |
| 268 | /// Alignment in bytes. |
| 269 | alignment: u32, |
| 270 | } |
| 271 | |
| 272 | /// Computed union layout parameters. |
| 273 | record UnionLayoutInfo: Copy { |
| 274 | layout: Layout, |
| 275 | valOffset: u32, |
| 276 | isAllVoid: bool, |
| 277 | } |
| 278 | |
| 279 | /// Pre-computed metadata for slice range expressions. |
| 280 | /// Used by the lowerer. |
| 281 | export record SliceRangeInfo: Copy { |
| 282 | /// Element type of the resulting slice. |
| 283 | itemType: *Type, |
| 284 | /// Whether the resulting slice is mutable. |
| 285 | mutable: bool, |
| 286 | /// Static capacity if container is an array. |
| 287 | capacity: ?u32, |
| 288 | } |
| 289 | |
| 290 | /// Pre-computed metadata for `for` loop iteration. |
| 291 | /// Used by the lowerer to avoid re-analyzing the iterable type. |
| 292 | export union ForLoopInfo: Copy { |
| 293 | /// Iterating over a range expression (e.g., `for i in 0..n`). |
| 294 | Range { |
| 295 | valType: *Type, |
| 296 | range: ast::Range, |
| 297 | bindingName: ?*[u8], |
| 298 | indexName: ?*[u8] |
| 299 | }, |
| 300 | /// Iterating over an array or slice. For arrays, the length field is set. |
| 301 | Collection { |
| 302 | elemType: *Type, |
| 303 | length: ?u32, |
| 304 | bindingName: ?*[u8], |
| 305 | indexName: ?*[u8] |
| 306 | }, |
| 307 | } |
| 308 | |
| 309 | /// Resolved function signature details. |
| 310 | export record FnType: Copy { |
| 311 | /// Symbolic regions declared by the source function. |
| 312 | regions: ?*RegionScope, |
| 313 | /// Parameter types in call order. |
| 314 | paramTypes: *[*Type], |
| 315 | /// Return value type. |
| 316 | returnType: *Type, |
| 317 | /// Error types that the function can throw. |
| 318 | throwList: *[*Type], |
| 319 | /// Whether calling this function requires an unsafe context. |
| 320 | isUnsafe: bool, |
| 321 | } |
| 322 | |
| 323 | /// Describes a type computed during semantic analysis. |
| 324 | export union Type: Copy { |
| 325 | /// A type that couldn't be decided. |
| 326 | Unknown, |
| 327 | /// Types only used during inference. |
| 328 | Nil, Undefined, Int, |
| 329 | /// Primitive types. |
| 330 | Void, Opaque, Never, Bool, |
| 331 | /// Integer types. |
| 332 | U8, U16, U32, U64, I8, I16, I32, I64, |
| 333 | /// Shared cell pointer with controlled payload access. |
| 334 | Cell { |
| 335 | /// Storage lifetime and ownership class. |
| 336 | class: types::PointerClass, |
| 337 | /// Optional compile-time permission region. |
| 338 | permission: ?*unsafe types::Region, |
| 339 | /// Payload type, preserved by all writes. |
| 340 | payload: *Type, |
| 341 | }, |
| 342 | /// Affine allocation interface retained by a lexical region. |
| 343 | Session(*unsafe types::Region), |
| 344 | /// Range types, eg. `start..end`. |
| 345 | Range { |
| 346 | start: ?*Type, |
| 347 | end: ?*Type, |
| 348 | }, |
| 349 | /// Owning pointer-like address. |
| 350 | Pointer { |
| 351 | class: types::PointerClass, |
| 352 | target: *Type, |
| 353 | mutable: bool, |
| 354 | }, |
| 355 | /// Owning slice. |
| 356 | Slice { |
| 357 | class: types::PointerClass, |
| 358 | item: *Type, |
| 359 | mutable: bool, |
| 360 | }, |
| 361 | /// Eg. `[i32; 32]`. |
| 362 | Array(ArrayType), |
| 363 | /// Eg. `?T`. |
| 364 | Optional(*Type), |
| 365 | /// Eg. `fn id(i32) -> i32`. |
| 366 | Fn(*FnType), |
| 367 | /// Named, ie. user-defined types, includes union variants. |
| 368 | Nominal(*unsafe NominalType), |
| 369 | /// Owning trait object. An erased type with v-table. |
| 370 | TraitObject { |
| 371 | /// Ownership and safety class. |
| 372 | class: types::PointerClass, |
| 373 | /// Trait definition. |
| 374 | traitInfo: *unsafe TraitType, |
| 375 | /// Whether the pointer is mutable. |
| 376 | mutable: bool, |
| 377 | }, |
| 378 | } |
| 379 | |
| 380 | /// Structured diagnostic payload for type mismatches. |
| 381 | export record TypeMismatch: Copy { |
| 382 | expected: Type, |
| 383 | actual: Type, |
| 384 | } |
| 385 | |
| 386 | /// Structured diagnostic payload for invalid `as` casts. |
| 387 | export record InvalidAsCast: Copy { |
| 388 | from: Type, |
| 389 | to: Type, |
| 390 | } |
| 391 | |
| 392 | /// Diagnostic payload for argument count mismatches. |
| 393 | export record CountMismatch: Copy { |
| 394 | expected: u32, |
| 395 | actual: u32, |
| 396 | } |
| 397 | |
| 398 | /// Detailed payload attached to a symbol, specialized per symbol kind. |
| 399 | export union SymbolData: Copy { |
| 400 | /// Payload describing mutable bindings like variables or functions. |
| 401 | Value { |
| 402 | /// Whether the binding permits mutation. |
| 403 | mutable: bool, |
| 404 | /// Custom alignment requirement, or 0 for default. |
| 405 | alignment: u32, |
| 406 | /// Resolved type associated with the value. |
| 407 | type: Type, |
| 408 | /// Whether the variable's address is taken anywhere (via `&` or `&mut`). |
| 409 | /// Used by the lowerer to allocate a stack slot eagerly. |
| 410 | addressTaken: bool, |
| 411 | }, |
| 412 | /// Payload describing constants. |
| 413 | Constant { |
| 414 | /// Resolved type associated with the value. |
| 415 | type: Type, |
| 416 | /// Constant value, if any. |
| 417 | value: ?ConstValue, |
| 418 | }, |
| 419 | /// Payload describing union variants and the union type they instantiate. |
| 420 | Variant { |
| 421 | /// Variant payload type. |
| 422 | type: Type, |
| 423 | /// Union declaration. |
| 424 | decl: *ast::Node, |
| 425 | /// Variant ordinal in declaration order. |
| 426 | ordinal: u32, |
| 427 | /// Variant index within the union. |
| 428 | index: u32, |
| 429 | }, |
| 430 | /// Module reference. |
| 431 | Module { |
| 432 | /// Module entry in the graph. |
| 433 | entry: *module::ModuleEntry, |
| 434 | /// Module scope. |
| 435 | scope: *unsafe mut Scope, |
| 436 | }, |
| 437 | /// Payload describing type symbols with their resolved type. |
| 438 | Type(*unsafe mut NominalType), |
| 439 | /// Trait symbol. |
| 440 | Trait(*unsafe mut TraitType), |
| 441 | } |
| 442 | |
| 443 | /// Resolved symbol allocated during semantic analysis. |
| 444 | export record Symbol: Copy { |
| 445 | /// Unique identity within the resolver that created this symbol. |
| 446 | id: u32, |
| 447 | /// Symbol name in source code. |
| 448 | name: *[u8], |
| 449 | /// Data associated with the symbol. |
| 450 | data: SymbolData, |
| 451 | /// Bitset of attributes applied to the declaration. |
| 452 | attrs: u32, |
| 453 | /// AST node that introduced the symbol. |
| 454 | node: *ast::Node, |
| 455 | /// Module ID this symbol belongs to. Only for module-level symbols. |
| 456 | moduleId: ?u16, |
| 457 | } |
| 458 | |
| 459 | /// Integer constant payload. |
| 460 | export record ConstInt: Copy { |
| 461 | /// Absolute magnitude of the value. |
| 462 | magnitude: u64, |
| 463 | /// Bit width of the integer. |
| 464 | bits: u8, |
| 465 | /// Whether the integer is signed. |
| 466 | signed: bool, |
| 467 | /// Whether the value is negative (only valid when `signed` is true). |
| 468 | negative: bool, |
| 469 | } |
| 470 | |
| 471 | /// Constant value recorded for literal nodes. |
| 472 | export union ConstValue: Copy { |
| 473 | Bool(bool), |
| 474 | Char(u8), |
| 475 | String(*[u8]), |
| 476 | Int(ConstInt), |
| 477 | } |
| 478 | |
| 479 | /// Integer range metadata for primitive integer types. |
| 480 | union IntegerRange: Copy { |
| 481 | Signed { |
| 482 | bits: u8, |
| 483 | min: i64, |
| 484 | max: i64, |
| 485 | lim: u64, |
| 486 | }, |
| 487 | Unsigned { |
| 488 | bits: u8, |
| 489 | max: u64, |
| 490 | }, |
| 491 | } |
| 492 | |
| 493 | /// Diagnostic emitted by the analyzer. |
| 494 | export record Error: Copy { |
| 495 | /// Error category. |
| 496 | kind: ErrorKind, |
| 497 | /// Node associated with the error, if known. |
| 498 | node: ?*ast::Node, |
| 499 | /// Module ID where this error occurred. |
| 500 | moduleId: u16, |
| 501 | } |
| 502 | |
| 503 | /// High-level classification for semantic diagnostics. |
| 504 | export union ErrorKind: Copy { |
| 505 | /// Identifier declared more than once in the same scope. |
| 506 | DuplicateBinding(*[u8]), |
| 507 | /// Identifier referenced before it was declared. |
| 508 | UnresolvedSymbol(*[u8]), |
| 509 | /// Attempted to assign to an immutable binding. |
| 510 | ImmutableBinding, |
| 511 | /// Slice append requires a valid allocator record and callback. |
| 512 | InvalidSliceAllocator, |
| 513 | /// Expected a compile-time constant expression. |
| 514 | ConstExprRequired, |
| 515 | /// Symbol arena exhausted while binding identifiers. |
| 516 | SymbolOverflow, |
| 517 | /// Expression has the wrong type. |
| 518 | TypeMismatch(TypeMismatch), |
| 519 | /// Numeric literal does not fit within the required range. |
| 520 | NumericLiteralOverflow, |
| 521 | /// Record literal omitted a required field. |
| 522 | RecordFieldMissing(*[u8]), |
| 523 | /// Record representation is private to another module. |
| 524 | OpaqueRecordAccess, |
| 525 | /// Record literal referenced a field that does not exist. |
| 526 | RecordFieldUnknown(*[u8]), |
| 527 | /// Brace syntax used on unlabeled record. |
| 528 | RecordFieldStyleMismatch, |
| 529 | /// Record literal supplied the wrong number of fields. |
| 530 | RecordFieldCountMismatch(CountMismatch), |
| 531 | /// Record literal fields not in declaration order. |
| 532 | RecordFieldOutOfOrder { field: *[u8], prev: *[u8] }, |
| 533 | /// Function call supplied the wrong number of arguments. |
| 534 | FnArgCountMismatch(CountMismatch), |
| 535 | /// Function throws list has the wrong number of types. |
| 536 | FnThrowCountMismatch(CountMismatch), |
| 537 | /// Expected an identifier node. |
| 538 | ExpectedIdentifier, |
| 539 | /// Expected any optional type. |
| 540 | ExpectedOptional, |
| 541 | /// Expected a numeric type. |
| 542 | ExpectedNumeric, |
| 543 | /// Expected a pointer type. |
| 544 | ExpectedPointer, |
| 545 | /// Expected a record type. |
| 546 | ExpectedRecord, |
| 547 | /// Expected an array or slice value. |
| 548 | ExpectedIndexable, |
| 549 | /// Expected an iterable (array, slice, or range) for a `for` loop. |
| 550 | ExpectedIterable, |
| 551 | /// Invalid `as` cast between the provided types. |
| 552 | InvalidAsCast(InvalidAsCast), |
| 553 | /// Invalid alignment value specified. |
| 554 | InvalidAlignmentValue(u32), |
| 555 | /// Invalid module path. |
| 556 | InvalidModulePath, |
| 557 | /// Invalid identifier. |
| 558 | InvalidIdentifier(*ast::Node), |
| 559 | /// Placeholder used where a value expression is required. |
| 560 | PlaceholderExpression, |
| 561 | /// Invalid scope access. |
| 562 | InvalidScopeAccess, |
| 563 | /// Referenced an unknown array field. |
| 564 | ArrayFieldUnknown(*[u8]), |
| 565 | /// Referenced an unknown slice field. |
| 566 | SliceFieldUnknown(*[u8]), |
| 567 | /// Array slicing without taking an address. |
| 568 | SliceRequiresAddress, |
| 569 | /// Slice bounds exceed array length. |
| 570 | SliceRangeOutOfBounds, |
| 571 | /// Unexpected `return` statement. |
| 572 | UnexpectedReturn, |
| 573 | /// Unexpected module name. |
| 574 | UnexpectedModuleName, |
| 575 | /// Unexpected node. |
| 576 | UnexpectedNode(*ast::Node), |
| 577 | /// Function with non-void return type falls through without returning. |
| 578 | FnMissingReturn, |
| 579 | /// Function is missing a body. |
| 580 | FnMissingBody, |
| 581 | /// Function body is not expected. |
| 582 | FnUnexpectedBody, |
| 583 | /// Intrinsic function must not have a body. |
| 584 | IntrinsicUnexpectedBody, |
| 585 | /// Encountered loop control outside of a loop construct. |
| 586 | InvalidLoopControl, |
| 587 | /// `try` used when the enclosing function does not declare throws. |
| 588 | TryRequiresThrows, |
| 589 | /// `try` used to propagate an error not declared by the enclosing function. |
| 590 | TryIncompatibleError, |
| 591 | /// `throw` used when the enclosing function does not declare throws. |
| 592 | ThrowRequiresThrows, |
| 593 | /// `throw` used with an error type not declared by the enclosing function. |
| 594 | ThrowIncompatibleError, |
| 595 | /// `try` applied to an expression that cannot throw. |
| 596 | TryNonThrowing, |
| 597 | /// Inferred catch binding used with multi-error callee. |
| 598 | TryCatchMultiError, |
| 599 | /// Duplicate error type in typed catch clauses. |
| 600 | TryCatchDuplicateType, |
| 601 | /// Distinct error types have the same tag after region erasure. |
| 602 | AmbiguousRegionalError, |
| 603 | /// Typed catch clauses do not cover all error types. |
| 604 | TryCatchNonExhaustive, |
| 605 | /// Called a fallible function without using `try`. |
| 606 | MissingTry, |
| 607 | /// Cannot use opaque type in this context. |
| 608 | OpaqueTypeNotAllowed, |
| 609 | /// Cannot dereference pointer to opaque type. |
| 610 | OpaqueTypeDeref, |
| 611 | /// Cannot perform pointer arithmetic on opaque pointer. |
| 612 | OpaquePointerArithmetic, |
| 613 | /// Cannot infer type from context. |
| 614 | CannotInferType, |
| 615 | /// Cannot assign a void value to a variable. |
| 616 | CannotAssignVoid, |
| 617 | /// `default` attribute used on a non-function declaration. |
| 618 | DefaultAttrOnlyOnFn, |
| 619 | /// Union variant requires a payload but none was provided. |
| 620 | UnionVariantPayloadMissing(*[u8]), |
| 621 | /// Union variant does not expect a payload but one was provided. |
| 622 | UnionVariantPayloadUnexpected(*[u8]), |
| 623 | /// `match` on a union omits a variant without a `default` case. |
| 624 | UnionMatchNonExhaustive(*[u8]), |
| 625 | /// `match` on an optional is missing a value case. |
| 626 | OptionalMatchMissingValue, |
| 627 | /// `match` on an optional is missing a nil case. |
| 628 | OptionalMatchMissingNil, |
| 629 | /// `match` on a bool is missing a case (true or false). |
| 630 | BoolMatchMissing(bool), |
| 631 | /// `match` on a non-union type is missing a catch-all. |
| 632 | MatchNonExhaustive, |
| 633 | /// `match` has more than one catch-all prongs. |
| 634 | DuplicateCatchAll, |
| 635 | /// `match` has a prong after an unguarded catch-all. |
| 636 | CatchAllMustBeLast, |
| 637 | /// `match` has a duplicate case pattern. |
| 638 | DuplicateMatchPattern, |
| 639 | /// `match` has an unreachable `else`: all cases are already handled. |
| 640 | UnreachableElse, |
| 641 | /// Builtin called with wrong number of arguments. |
| 642 | BuiltinArgCountMismatch(CountMismatch), |
| 643 | /// Instance method receiver mutability does not match the trait declaration. |
| 644 | ReceiverMutabilityMismatch, |
| 645 | /// Duplicate instance declaration for the same (trait, type) pair. |
| 646 | DuplicateInstance, |
| 647 | /// Instance declaration is missing a required trait method. |
| 648 | MissingTraitMethod(*[u8]), |
| 649 | /// Trait name used as a value expression. |
| 650 | UnexpectedTraitName, |
| 651 | /// Trait method receiver does not point to the declaring trait. |
| 652 | TraitReceiverMismatch, |
| 653 | /// Trait declaration and instance disagree about unsafe call requirements. |
| 654 | TraitMethodSafetyMismatch, |
| 655 | /// Function declaration has too many parameters. |
| 656 | FnParamOverflow(CountMismatch), |
| 657 | /// Function declaration has too many throws. |
| 658 | FnThrowOverflow(CountMismatch), |
| 659 | /// Trait declaration has too many methods. |
| 660 | TraitMethodOverflow(CountMismatch), |
| 661 | /// Instance declaration is missing a required supertrait instance. |
| 662 | MissingSupertraitInstance(*[u8]), |
| 663 | /// An affine binding was used after it moved. |
| 664 | AffineUseAfterMove(*[u8]), |
| 665 | /// Linear binding was consumed more than once. |
| 666 | LinearUseAfterConsume(*[u8]), |
| 667 | /// Linear binding remains available at an exit. |
| 668 | LinearNotConsumed(*[u8]), |
| 669 | /// A case-pattern `let-else` fallback must terminate control flow. |
| 670 | LinearLetElseMustTerminate, |
| 671 | /// Branches disagree about a linear binding's state. |
| 672 | LinearBranchMismatch(*[u8]), |
| 673 | /// A linear field cannot be moved independently. |
| 674 | LinearPartialMove, |
| 675 | /// A linear value cannot be discarded. |
| 676 | LinearDiscard, |
| 677 | /// Assignment would overwrite a live linear value. |
| 678 | LinearOverwrite, |
| 679 | /// `undefined` cannot initialize a linear type. |
| 680 | LinearUndefined, |
| 681 | /// A `Copy` declaration contains a non-copy field or variant. |
| 682 | CopyContainsNonCopy, |
| 683 | /// A declaration carries both `Copy` and `Once`. |
| 684 | ConflictingOwnershipMarkers, |
| 685 | /// A region name is not visible in this declaration or block. |
| 686 | UnknownRegion(*[u8]), |
| 687 | /// A region application has the wrong argument count. |
| 688 | RegionArgumentCount(CountMismatch), |
| 689 | /// A region parameter has no consistent argument from checked references. |
| 690 | RegionInference(*[u8]), |
| 691 | /// A region argument does not satisfy its declared parent relation. |
| 692 | RegionParent(*[u8]), |
| 693 | /// A region parent relation contains a cycle. |
| 694 | RegionCycle(*[u8]), |
| 695 | /// A value retains a region that has left lexical scope. |
| 696 | RegionEscape(*[u8]), |
| 697 | /// A session requires one exclusive borrow of an allocation trait implementer. |
| 698 | InvalidSessionSource, |
| 699 | /// Allocation requires a value that can be discarded without destruction. |
| 700 | InvalidAllocationValue, |
| 701 | /// Cell payload does not satisfy storage and ownership requirements. |
| 702 | InvalidCellPayload, |
| 703 | /// Cell payload access lacks matching lexical permission authority. |
| 704 | CellPermissionRequired(*[u8]), |
| 705 | /// The allocated value has an invalid or overflowing layout. |
| 706 | InvalidAllocationLayout, |
| 707 | /// A compiler-known allocation trait method has an invalid signature. |
| 708 | InvalidAllocationRuntime, |
| 709 | /// The function has too many distinct full-region projections. |
| 710 | RegionalLoanOverflow, |
| 711 | /// A nominal value layout contains itself. |
| 712 | RecursiveType, |
| 713 | /// A reference appears in a storable or escaping position. |
| 714 | InvalidRefPosition, |
| 715 | /// A reference local requires a fixed binding to existing storage. |
| 716 | RefBinding, |
| 717 | /// Call arguments contain overlapping incompatible loans. |
| 718 | BorrowConflict(*[u8]), |
| 719 | /// Unsafe operation outside an unsafe context. |
| 720 | UnsafeOperation, |
| 721 | /// An unsafe call requires an unsafe context. |
| 722 | UnsafeCall, |
| 723 | /// Internal error. |
| 724 | Internal, |
| 725 | } |
| 726 | |
| 727 | /// Diagnostics returned by the analyzer. |
| 728 | export record Diagnostics: Copy { |
| 729 | /// Immutable errors captured at the end of an analysis operation. |
| 730 | errors: *[Error], |
| 731 | } |
| 732 | |
| 733 | /// Mutable diagnostic storage owned by a resolver. |
| 734 | record DiagnosticBuffer { |
| 735 | /// Backing entries. Only the prefix below `len` is initialized. |
| 736 | entries: *mut [Error], |
| 737 | /// Number of recorded errors. |
| 738 | len: u32, |
| 739 | } |
| 740 | |
| 741 | /// Call context. |
| 742 | union CallCtx: Copy { |
| 743 | /// Normal function call. |
| 744 | Normal, |
| 745 | /// Fallible function call, ie. `try f()`. |
| 746 | Try, |
| 747 | } |
| 748 | |
| 749 | /// Result of resolving a record literal's type name. |
| 750 | record ResolvedRecordLitType: Copy { |
| 751 | /// The record nominal type to use for field checking. |
| 752 | recordType: *unsafe NominalType, |
| 753 | /// The result type of the literal (record type or union type for variants). |
| 754 | resultType: Type, |
| 755 | } |
| 756 | |
| 757 | /// Result of checking for a `super` path prefix. |
| 758 | record SuperAccessResult: Copy { |
| 759 | scope: *unsafe mut Scope, |
| 760 | child: *ast::Node, |
| 761 | } |
| 762 | |
| 763 | /// Initialization operation performed after session storage reservation. |
| 764 | export union SessionAllocationKind: Copy { |
| 765 | /// Initialize one object from a value. |
| 766 | New, |
| 767 | /// Copy plain Copy elements from a slice. |
| 768 | Copy, |
| 769 | /// Fill a slice with a plain Copy value. |
| 770 | Fill, |
| 771 | } |
| 772 | |
| 773 | /// Typed session allocation and its checked runtime reservation function. |
| 774 | export record SessionAllocation: Copy { |
| 775 | /// Initialization operation. |
| 776 | kind: SessionAllocationKind, |
| 777 | /// Initialized element type. |
| 778 | item: *Type, |
| 779 | /// Allocation trait used by the session source. |
| 780 | traitInfo: *unsafe TraitType, |
| 781 | /// Reservation method slot in the allocation trait. |
| 782 | methodIndex: u32, |
| 783 | } |
| 784 | |
| 785 | /// Node-specific resolver metadata. |
| 786 | export union NodeExtra: Copy { |
| 787 | /// No extra data for this node. |
| 788 | None, |
| 789 | /// Region identities owned by a source declaration. |
| 790 | Regions(*RegionScope), |
| 791 | /// Resolved field index for record literal fields. |
| 792 | RecordField { index: u32 }, |
| 793 | /// Slice range metadata for subscript expressions with ranges. |
| 794 | SliceRange(SliceRangeInfo), |
| 795 | /// Cached union variant metadata for patterns/constructors. |
| 796 | UnionVariant { ordinal: u32, tag: u32 }, |
| 797 | /// Match prong metadata. |
| 798 | MatchProng { catchAll: bool }, |
| 799 | /// Match expression metadata. |
| 800 | Match { isConst: bool }, |
| 801 | /// For-loop iteration metadata. |
| 802 | ForLoop(ForLoopInfo), |
| 803 | /// Trait method call metadata. |
| 804 | TraitMethodCall { |
| 805 | /// Trait definition. |
| 806 | traitInfo: *unsafe TraitType, |
| 807 | /// Method index in the v-table. |
| 808 | methodIndex: u32, |
| 809 | }, |
| 810 | /// Standalone method call metadata. |
| 811 | MethodCall { method: *unsafe MethodEntry }, |
| 812 | /// Typed allocation through a session interface. |
| 813 | SessionAllocation(SessionAllocation), |
| 814 | /// Slice `.append(val, allocator)` method call. |
| 815 | SliceAppend { elemType: *Type }, |
| 816 | /// Slice `.delete(index)` method call. |
| 817 | SliceDelete { elemType: *Type }, |
| 818 | } |
| 819 | |
| 820 | /// Symbol identity and storage associated with a resolved AST node. |
| 821 | export record ResolvedSymbol: Copy { |
| 822 | /// Identity within the resolver that owns the node metadata. |
| 823 | id: u32, |
| 824 | /// Symbol storage used by type resolution and lowering. |
| 825 | symbol: *unsafe mut Symbol, |
| 826 | } |
| 827 | |
| 828 | /// Combined resolver metadata for a single AST node. |
| 829 | export record NodeData: Copy { |
| 830 | /// Number of local bindings and internal iteration variables in this function. |
| 831 | localCount: u32, |
| 832 | /// Resolved type for this node. |
| 833 | ty: Type, |
| 834 | /// Coercion plan applied to this node. |
| 835 | coercion: Coercion, |
| 836 | /// Symbol identity and storage associated with this node. |
| 837 | binding: ?ResolvedSymbol, |
| 838 | /// Constant value for literal nodes. |
| 839 | constValue: ?ConstValue, |
| 840 | /// Lexical scope owned by this node. |
| 841 | scope: ?*unsafe mut Scope, |
| 842 | /// Node-specific extra data. |
| 843 | extra: NodeExtra, |
| 844 | } |
| 845 | |
| 846 | /// Table storing all resolver metadata indexed by node ID. |
| 847 | record NodeDataTable { |
| 848 | /// Semantic data indexed by AST node ID. |
| 849 | entries: *mut [NodeData], |
| 850 | } |
| 851 | |
| 852 | /// Lexical scope. |
| 853 | export record Scope: Copy { |
| 854 | /// Owning AST node, or `nil` for the root scope. |
| 855 | owner: ?*ast::Node, |
| 856 | /// Parent/enclosing scope. |
| 857 | parent: ?*unsafe mut Scope, |
| 858 | /// Module ID if this is a module scope. |
| 859 | moduleId: ?u16, |
| 860 | /// Symbols introduced inside the scope, allocated from the arena. |
| 861 | symbols: *unsafe mut [*unsafe mut Symbol], |
| 862 | /// Number of live symbols. |
| 863 | symbolsLen: u32, |
| 864 | } |
| 865 | |
| 866 | /// An object used by the enter and exit functions for module scopes. |
| 867 | record ModuleScope: Copy { |
| 868 | /// Module root node. |
| 869 | root: *ast::Node, |
| 870 | /// Module entry in graph. |
| 871 | entry: *module::ModuleEntry, |
| 872 | /// The newly entered scope. |
| 873 | newScope: *unsafe mut Scope, |
| 874 | /// The previous scope. |
| 875 | prevScope: *unsafe mut Scope, |
| 876 | /// The previous module. |
| 877 | prevMod: u16, |
| 878 | } |
| 879 | |
| 880 | /// Loop context for tracking control flow within loops. |
| 881 | record LoopCtx: Copy { |
| 882 | /// Whether a reachable break was encountered in this loop. |
| 883 | /// This is used to determine whether a loop diverges. |
| 884 | hasBreak: bool, |
| 885 | } |
| 886 | |
| 887 | /// Configuration for semantic analysis. |
| 888 | export record Config: Copy { |
| 889 | /// Whether we're building in test mode. |
| 890 | buildTest: bool, |
| 891 | } |
| 892 | |
| 893 | /// How pattern bindings are created during match. |
| 894 | export union MatchBy: Copy { |
| 895 | /// Match by value. |
| 896 | Value, |
| 897 | /// Match by immutable reference. |
| 898 | Ref(types::PointerClass), |
| 899 | /// Match by mutable reference. |
| 900 | MutRef, |
| 901 | } |
| 902 | |
| 903 | /// State of a match statement being resolved. |
| 904 | // TODO: This is only used because of the maximum function param limitation. |
| 905 | record MatchState: Copy { |
| 906 | /// Is the match catch-all? |
| 907 | catchAll: bool, |
| 908 | /// Is the match constant? |
| 909 | isConst: bool |
| 910 | } |
| 911 | |
| 912 | /// Result of unwrapping a type for pattern matching. |
| 913 | export record MatchSubject: Copy { |
| 914 | /// The effective type to match against. |
| 915 | effectiveTy: Type, |
| 916 | /// How bindings should be created. |
| 917 | by: MatchBy, |
| 918 | } |
| 919 | |
| 920 | /// How an expression uses a linear result. |
| 921 | union LinearUse: Copy { |
| 922 | /// Consume the value and end its availability. |
| 923 | Consume, |
| 924 | /// Read the value without consuming it. |
| 925 | Observe, |
| 926 | /// Borrow the value through a reference. |
| 927 | Borrow, |
| 928 | /// Discard an unused expression result. |
| 929 | Discard, |
| 930 | /// Use the value as an assignment target. |
| 931 | Place, |
| 932 | /// Evaluate a place prefix after checking the complete place. |
| 933 | Locate, |
| 934 | } |
| 935 | |
| 936 | /// Region role sought during compile-time type traversal. |
| 937 | union RegionTypeRole: Copy { |
| 938 | /// Compile-time identity associated with a cell payload. |
| 939 | CellPermission, |
| 940 | /// Storage reference that can carry a regional borrow. |
| 941 | Reference, |
| 942 | /// Interior storage that can retain a reference after a call returns. |
| 943 | Retention, |
| 944 | /// Region dependencies that must cover a destination. |
| 945 | StorageValidation, |
| 946 | /// Named storage retained by a value. |
| 947 | StoragePresence, |
| 948 | } |
| 949 | |
| 950 | /// Consumption rule for a tracked move-only binding. |
| 951 | union BindingUse: Copy { |
| 952 | /// The binding can be consumed at most once. |
| 953 | Affine, |
| 954 | /// The binding must be consumed exactly once. |
| 955 | Linear, |
| 956 | } |
| 957 | |
| 958 | /// Resolved binding metadata retained for ownership checks and diagnostics. |
| 959 | record TrackedSymbol: Copy { |
| 960 | /// Resolver-local symbol identity. |
| 961 | id: u32, |
| 962 | /// Source name used in ownership diagnostics. |
| 963 | name: *[u8], |
| 964 | /// Declaration used to locate an unconsumed binding. |
| 965 | node: *ast::Node, |
| 966 | /// Consumption rule fixed before ownership analysis. |
| 967 | usage: BindingUse, |
| 968 | } |
| 969 | |
| 970 | /// Per-control-flow-path ownership state. |
| 971 | /// Active binding slots below `len` must contain metadata. |
| 972 | record LinearEnv: Copy { |
| 973 | /// Active full-region loans, indexed by the checker's regional loan table. |
| 974 | regionalLoans: u64, |
| 975 | /// Initialized slots for resolved binding metadata. |
| 976 | symbols: [?TrackedSymbol; MAX_LINEAR_BINDINGS], |
| 977 | /// Bit set for each binding that remains available. |
| 978 | available: u64, |
| 979 | /// Number of active binding slots in `symbols`. |
| 980 | len: u32, |
| 981 | /// Whether this control-flow path has terminated. |
| 982 | terminated: bool, |
| 983 | } |
| 984 | |
| 985 | /// A storage root and its statically distinct record fields. |
| 986 | record BorrowPlace: Copy { |
| 987 | /// Symbol that owns or supplies the storage. |
| 988 | root: ?*unsafe mut Symbol, |
| 989 | /// Field indices before the first uncertain projection. |
| 990 | fields: [u32; MAX_BORROW_FIELDS], |
| 991 | /// Number of initialized field indices. |
| 992 | len: u32, |
| 993 | /// Whether further projections can identify distinct storage. |
| 994 | precise: bool, |
| 995 | } |
| 996 | /// A reference binding that protects its source for one lexical scope. |
| 997 | record LocalLoan: Copy { |
| 998 | /// Local symbol that provides access, or nil for a pending call argument. |
| 999 | binding: ?*unsafe mut Symbol, |
| 1000 | /// Storage retained by the reference. |
| 1001 | place: BorrowPlace, |
| 1002 | /// Whether other reads of the source are excluded. |
| 1003 | exclusive: bool, |
| 1004 | /// Permission identity protected independently of the source place. |
| 1005 | permission: ?*unsafe types::Region, |
| 1006 | /// Named region that owns associated cell storage, when one exists. |
| 1007 | storage: ?*unsafe types::Region, |
| 1008 | } |
| 1009 | |
| 1010 | /// Argument metadata retained during call-scoped conflict checks. |
| 1011 | record CallArgument: Copy { |
| 1012 | /// Receiver or explicit argument expression. |
| 1013 | node: *ast::Node, |
| 1014 | /// Whether overlapping argument access is excluded. |
| 1015 | exclusive: bool, |
| 1016 | } |
| 1017 | /// Function-local exact-use checker state. |
| 1018 | /// Read loop arrays only at indices below `loopDepth`. |
| 1019 | /// `enterLinearLoop` initializes each slot before it increases `loopDepth`. |
| 1020 | record LinearChecker: 'arena + 'checking where 'arena: 'checking { |
| 1021 | /// Resolver that owns the symbols and diagnostics. |
| 1022 | resolver: &'checking mut Resolver 'arena, |
| 1023 | /// Regional projections discovered in this function. |
| 1024 | regional: [?RegionalLoan; MAX_REGIONAL_LOANS], |
| 1025 | /// Number of active regional loan entries. |
| 1026 | regionalLen: u32, |
| 1027 | /// Named regions active at the current source location. |
| 1028 | regions: ?*RegionScope, |
| 1029 | /// Regional loans carried to each loop's next iteration. |
| 1030 | loopBackLoans: [u64; MAX_LINEAR_LOOP_DEPTH], |
| 1031 | /// Regional loans carried to each loop's exits. |
| 1032 | loopExitLoans: [u64; MAX_LINEAR_LOOP_DEPTH], |
| 1033 | /// Regions active at each loop's entry and exit. |
| 1034 | loopRegions: [?*RegionScope; MAX_LINEAR_LOOP_DEPTH], |
| 1035 | /// Source places protected by active pattern references. |
| 1036 | loans: [BorrowPlace; MAX_LINEAR_BINDINGS], |
| 1037 | /// Number of active entries in `loans`. |
| 1038 | loanLen: u32, |
| 1039 | /// Reference locals in active lexical scopes. |
| 1040 | locals: [LocalLoan; MAX_LINEAR_BINDINGS], |
| 1041 | /// Number of active local loans. |
| 1042 | localLen: u32, |
| 1043 | /// Permission identities controlled by active lexical authority bindings. |
| 1044 | authorityPermissions: [?*unsafe types::Region; MAX_LINEAR_BINDINGS], |
| 1045 | /// Bindings that provide each lexical permission authority. |
| 1046 | authorityBindings: [?*unsafe mut Symbol; MAX_LINEAR_BINDINGS], |
| 1047 | /// Whether each lexical authority permits exclusive payload access. |
| 1048 | authorityExclusive: [bool; MAX_LINEAR_BINDINGS], |
| 1049 | /// Number of initialized authority entries. |
| 1050 | authorityLen: u32, |
| 1051 | /// Associated payload address currently validated by a region header. |
| 1052 | payloadAddress: ?*ast::Node, |
| 1053 | /// Matching authority reborrow currently being checked. |
| 1054 | witnessPermission: ?*unsafe types::Region, |
| 1055 | /// Binding count at entry to each active loop. |
| 1056 | loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH], |
| 1057 | /// Available bindings at entry to each active loop. |
| 1058 | loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH], |
| 1059 | /// Available bindings shared by the exits from each active loop. |
| 1060 | loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH], |
| 1061 | /// Whether each active loop can exit without `break`. |
| 1062 | loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH], |
| 1063 | /// Whether each active loop contains a reachable `break`. |
| 1064 | loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH], |
| 1065 | /// Number of active loops. |
| 1066 | loopDepth: u32, |
| 1067 | } |
| 1068 | |
| 1069 | /// Unwrap a pointer type for pattern matching. |
| 1070 | export fn unwrapMatchSubject(ty: Type) -> MatchSubject { |
| 1071 | if let case Type::Pointer { class, target, mutable } = ty { |
| 1072 | let mut bindingClass = types::PointerClass::Ref; |
| 1073 | if let case types::PointerClass::Region(_) = class { |
| 1074 | set bindingClass = class; |
| 1075 | } |
| 1076 | let by = MatchBy::MutRef if mutable else MatchBy::Ref(bindingClass); |
| 1077 | return MatchSubject { effectiveTy: *target, by }; |
| 1078 | } |
| 1079 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
| 1080 | } |
| 1081 | |
| 1082 | /// Source nodes that define the identities in one region environment. |
| 1083 | export union RegionDeclarations: Copy { |
| 1084 | /// Declaration parameters, including any non-region constraints. |
| 1085 | Parameters(*[*ast::Node]), |
| 1086 | /// Single region introduced by a lexical block. |
| 1087 | Block(*ast::Node), |
| 1088 | } |
| 1089 | |
| 1090 | /// Region names introduced by a declaration or lexical block. |
| 1091 | export record RegionScope: Copy { |
| 1092 | /// Immutable source declarations that supply region identities. |
| 1093 | declarations: RegionDeclarations, |
| 1094 | /// Entries in declaration order. |
| 1095 | entries: *unsafe [*unsafe mut types::Region], |
| 1096 | /// Enclosing lexical region environment. |
| 1097 | parent: ?*RegionScope, |
| 1098 | } |
| 1099 | |
| 1100 | /// Region arguments for one source declaration. |
| 1101 | record RegionSubstitution: Copy { |
| 1102 | /// Declared parameters in source order. |
| 1103 | parameters: *RegionScope, |
| 1104 | /// Inferred or explicit arguments. Every entry must be set before substitution. |
| 1105 | arguments: *unsafe mut [?*unsafe types::Region], |
| 1106 | } |
| 1107 | |
| 1108 | /// One interned application of a nominal declaration to exact region arguments. |
| 1109 | export record NominalApplication: Copy { |
| 1110 | /// Canonical source declaration identity. |
| 1111 | base: *unsafe NominalType, |
| 1112 | /// Source parameters in declaration order. |
| 1113 | parameters: *RegionScope, |
| 1114 | /// Region arguments in parameter order. |
| 1115 | arguments: *unsafe [*unsafe types::Region], |
| 1116 | /// Stable descriptor for the substituted field or variant view. |
| 1117 | view: *unsafe mut NominalType, |
| 1118 | /// Resolver-private generation of the most recent compile-time traversal. |
| 1119 | traversalGeneration: u32, |
| 1120 | /// Resolver-private region roles visited during that traversal. |
| 1121 | traversalRoles: u8, |
| 1122 | /// Next application in the resolver cache. |
| 1123 | next: ?*unsafe mut NominalApplication, |
| 1124 | /// Next application in the same lookup bucket. |
| 1125 | bucketNext: ?*unsafe mut NominalApplication, |
| 1126 | } |
| 1127 | |
| 1128 | /// Global resolver state. |
| 1129 | export record Resolver: 'arena { |
| 1130 | /// Number of symbol identities allocated by this resolver. |
| 1131 | symbolCount: u32, |
| 1132 | /// Active region names for source type checking. |
| 1133 | regionScope: ?*RegionScope, |
| 1134 | /// Interned applications of nominal region parameters. |
| 1135 | applications: ?*unsafe mut NominalApplication, |
| 1136 | /// First entry in the fully resolved suffix of the application list. |
| 1137 | completedApplications: ?*unsafe mut NominalApplication, |
| 1138 | /// Exact application lookup chains, backed by the resolver arena. |
| 1139 | applicationBuckets: *unsafe mut [?*unsafe mut NominalApplication], |
| 1140 | /// Monotonic identity for cycle-safe compile-time type traversals. |
| 1141 | nominalTraversalGeneration: u32, |
| 1142 | /// Cell payload checks that require complete nominal layouts. |
| 1143 | cellChecks: *mut [CellCheck], |
| 1144 | /// Current scope. |
| 1145 | scope: *unsafe mut Scope, |
| 1146 | /// Package scope containing package roots and top-level symbols. |
| 1147 | pkgScope: *unsafe mut Scope, |
| 1148 | /// Stack of loop contexts for nested loops. |
| 1149 | loopStack: [LoopCtx; MAX_LOOP_DEPTH], |
| 1150 | /// Current loop depth, indexes into loop stack. |
| 1151 | loopDepth: u32, |
| 1152 | /// Signature of the function currently being analyzed. |
| 1153 | currentFn: ?FnType, |
| 1154 | /// Declaration that owns the active function body and its local bindings. |
| 1155 | currentFnNode: ?*ast::Node, |
| 1156 | /// Current module being analyzed. |
| 1157 | currentMod: u16, |
| 1158 | /// Whether the current lexical context permits unsafe operations. |
| 1159 | inUnsafeContext: bool, |
| 1160 | /// Configuration for semantic analysis. |
| 1161 | config: Config, |
| 1162 | /// Caller-owned arena, valid for this resolver and all emitted metadata. |
| 1163 | arena: &'arena mut alloc::Arena, |
| 1164 | /// Combined semantic metadata table indexed by node ID. |
| 1165 | nodeData: NodeDataTable, |
| 1166 | /// Lookup chains for interned types. |
| 1167 | types: *unsafe mut [?*TypeNode], |
| 1168 | /// Diagnostics recorded so far. |
| 1169 | errors: DiagnosticBuffer, |
| 1170 | /// Stable module identities indexed by module ID. |
| 1171 | moduleEntries: [?*module::ModuleEntry; module::MAX_MODULES], |
| 1172 | /// Parsed roots captured while module update authority is available. |
| 1173 | moduleRoots: [?*ast::Node; module::MAX_MODULES], |
| 1174 | /// Cache of module scopes indexed by module ID. |
| 1175 | moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES], |
| 1176 | /// Trait instance registry. |
| 1177 | instances: [InstanceEntry; MAX_INSTANCES], |
| 1178 | /// Number of registered instances. |
| 1179 | instancesLen: u32, |
| 1180 | /// Standalone method registry. |
| 1181 | methods: [MethodEntry; MAX_METHODS], |
| 1182 | /// Number of registered standalone methods. |
| 1183 | methodsLen: u32, |
| 1184 | } |
| 1185 | |
| 1186 | /// Deferred cell payload validation that requires a complete nominal layout. |
| 1187 | record CellCheck: Copy { |
| 1188 | /// Source signature used for diagnostics. |
| 1189 | node: *ast::Node, |
| 1190 | /// Payload whose layout and ownership must be checked. |
| 1191 | payload: Type, |
| 1192 | /// Permission association that makes affine payloads eligible. |
| 1193 | permission: ?*unsafe types::Region, |
| 1194 | /// Region environment at the source signature. |
| 1195 | regions: ?*RegionScope, |
| 1196 | /// Module that owns the source signature. |
| 1197 | moduleId: u16, |
| 1198 | } |
| 1199 | |
| 1200 | /// Internal error sentinel thrown when analysis cannot proceed. |
| 1201 | export union ResolveError: Copy { |
| 1202 | Failure, |
| 1203 | } |
| 1204 | |
| 1205 | /// Node in a type interning lookup chain. |
| 1206 | record TypeNode: Copy { |
| 1207 | /// Exact interned type value. |
| 1208 | ty: Type, |
| 1209 | /// Next type in the same lookup bucket. |
| 1210 | next: ?*TypeNode, |
| 1211 | } |
| 1212 | |
| 1213 | /// Look up a region name in a lexical environment. |
| 1214 | unsafe fn findRegion(scope: ?*RegionScope, name: *[u8]) -> ?*unsafe mut types::Region { |
| 1215 | let mut current = scope; |
| 1216 | while let env = current { |
| 1217 | for region in env.entries { |
| 1218 | if mem::eq(region.name, name) { |
| 1219 | return region; |
| 1220 | } |
| 1221 | } |
| 1222 | set current = env.parent; |
| 1223 | } |
| 1224 | return nil; |
| 1225 | } |
| 1226 | |
| 1227 | /// Resolve a source region name without using its spelling as an identity. |
| 1228 | unsafe fn resolveRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe types::Region |
| 1229 | throws (ResolveError) |
| 1230 | { |
| 1231 | let case ast::NodeValue::Region { name, .. } = node.value |
| 1232 | else panic "resolveRegion: invalid region node"; |
| 1233 | let region = findRegion(self.regionScope, name) |
| 1234 | else throw emitError(self, node, ErrorKind::UnknownRegion(name)); |
| 1235 | return region; |
| 1236 | } |
| 1237 | |
| 1238 | /// Bind all region parameters before resolving their parent relations. |
| 1239 | unsafe fn bindRegions 'arena (self: &mut Resolver 'arena, owner: *ast::Node, nodes: *[*ast::Node]) -> ?*RegionScope |
| 1240 | throws (ResolveError) |
| 1241 | { |
| 1242 | if let case NodeExtra::Regions(scope) = self.nodeData.entries[owner.id].extra { |
| 1243 | return scope; |
| 1244 | } |
| 1245 | let mut count: u32 = 0; |
| 1246 | for node in nodes { |
| 1247 | if let case ast::NodeValue::Region { .. } = node.value { |
| 1248 | set count += 1; |
| 1249 | } |
| 1250 | } |
| 1251 | if count == 0 { |
| 1252 | return nil; |
| 1253 | } |
| 1254 | let entries = try! alloc::allocRawSlice( |
| 1255 | self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), count |
| 1256 | ) as *unsafe mut [*unsafe mut types::Region]; |
| 1257 | let mut index: u32 = 0; |
| 1258 | for node in nodes { |
| 1259 | let case ast::NodeValue::Region { name, .. } = node.value else continue; |
| 1260 | for i in 0..index { |
| 1261 | if mem::eq(entries[i].name, name) { |
| 1262 | throw emitError(self, node, ErrorKind::DuplicateBinding(name)); |
| 1263 | } |
| 1264 | } |
| 1265 | let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region)) |
| 1266 | as *unsafe mut types::Region; |
| 1267 | set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil }; |
| 1268 | set entries[index] = region; |
| 1269 | set index += 1; |
| 1270 | } |
| 1271 | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
| 1272 | set *scope = RegionScope { |
| 1273 | declarations: RegionDeclarations::Parameters(nodes), |
| 1274 | entries, |
| 1275 | parent: nil, |
| 1276 | }; |
| 1277 | let frozen: *RegionScope = scope; |
| 1278 | set index = 0; |
| 1279 | for node in nodes { |
| 1280 | let case ast::NodeValue::Region { parent, .. } = node.value else continue; |
| 1281 | if let parentNode = parent { |
| 1282 | let case ast::NodeValue::Region { name, .. } = parentNode.value |
| 1283 | else panic "bindRegions: invalid parent node"; |
| 1284 | let target = findRegion(frozen, name) |
| 1285 | else throw emitError(self, parentNode, ErrorKind::UnknownRegion(name)); |
| 1286 | if types::regionContains(entries[index], target) { |
| 1287 | throw emitError(self, parentNode, ErrorKind::RegionCycle(entries[index].name)); |
| 1288 | } |
| 1289 | set entries[index].parent = target; |
| 1290 | } |
| 1291 | set index += 1; |
| 1292 | } |
| 1293 | set self.nodeData.entries[owner.id].extra = NodeExtra::Regions(frozen); |
| 1294 | return frozen; |
| 1295 | } |
| 1296 | |
| 1297 | /// Mix aligned metadata addresses into a lookup key. |
| 1298 | fn addressHash(address: u64) -> u32 { |
| 1299 | return ((address >> 3) as u32) ^ ((address >> 35) as u32); |
| 1300 | } |
| 1301 | |
| 1302 | /// Hash the active fields of a pointer class. |
| 1303 | unsafe fn classHash(class: types::PointerClass) -> u32 { |
| 1304 | match class { |
| 1305 | case types::PointerClass::Owned => return 1, |
| 1306 | case types::PointerClass::Ref => return 2, |
| 1307 | case types::PointerClass::Unsafe => return 3, |
| 1308 | case types::PointerClass::Region(region) => return addressHash(region as u64), |
| 1309 | } |
| 1310 | } |
| 1311 | |
| 1312 | /// Hash active type fields so equal values select the same lookup chain. |
| 1313 | unsafe fn typeHash(ty: Type) -> u32 { |
| 1314 | match ty { |
| 1315 | case Type::Cell { class, permission, payload } => { |
| 1316 | let mut hash = addressHash(payload as u64) ^ (classHash(class) * CACHE_HASH_PRIME); |
| 1317 | if let region = permission { |
| 1318 | set hash = (hash ^ addressHash(region as u64)) * CACHE_HASH_PRIME; |
| 1319 | } |
| 1320 | return hash; |
| 1321 | } |
| 1322 | case Type::Pointer { class, target, mutable } => |
| 1323 | return addressHash(target as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0), |
| 1324 | case Type::Slice { class, item, mutable } => |
| 1325 | return addressHash(item as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0), |
| 1326 | case Type::TraitObject { class, traitInfo, mutable } => |
| 1327 | return addressHash(traitInfo as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0), |
| 1328 | case Type::Session(region) => return addressHash(region as u64), |
| 1329 | case Type::Optional(inner) => return addressHash(inner as u64), |
| 1330 | case Type::Fn(info) => return addressHash(info as u64), |
| 1331 | case Type::Nominal(info) => return addressHash(info as u64), |
| 1332 | case Type::Array(array) => return addressHash(array.item as u64) ^ (array.length * CACHE_HASH_PRIME), |
| 1333 | case Type::Range { start, end } => { |
| 1334 | let mut hash: u32 = 0; |
| 1335 | if let item = start { |
| 1336 | set hash = addressHash(item as u64); |
| 1337 | } |
| 1338 | if let item = end { |
| 1339 | set hash = hash ^ (addressHash(item as u64) * CACHE_HASH_PRIME); |
| 1340 | } |
| 1341 | return hash; |
| 1342 | } |
| 1343 | else => return 0, |
| 1344 | } |
| 1345 | } |
| 1346 | |
| 1347 | /// Allocate and intern a type in the arena, returning a pointer for deduplication. |
| 1348 | export unsafe fn allocType 'arena (self: &mut Resolver 'arena, ty: Type) -> *Type { |
| 1349 | // Search existing types for a match. |
| 1350 | let bucket = typeHash(ty) & (TYPE_BUCKETS - 1); |
| 1351 | let mut cursor = self.types[bucket]; |
| 1352 | while let node = cursor { |
| 1353 | if node.ty == ty { |
| 1354 | return &node.ty; |
| 1355 | } |
| 1356 | set cursor = node.next; |
| 1357 | } |
| 1358 | // Allocate a new type node from the arena. |
| 1359 | let node = try! alloc::alloc( |
| 1360 | &mut *self.arena, @sizeOf(TypeNode), @alignOf(TypeNode) |
| 1361 | ) as *mut TypeNode; |
| 1362 | |
| 1363 | set *node = TypeNode { ty, next: self.types[bucket] }; |
| 1364 | let frozen: *TypeNode = node; |
| 1365 | set self.types[bucket] = frozen; |
| 1366 | |
| 1367 | return &frozen.ty; |
| 1368 | } |
| 1369 | |
| 1370 | /// Allocate a nominal type descriptor and return a pointer to it. |
| 1371 | unsafe fn allocNominalType 'arena (self: &mut Resolver 'arena, info: NominalType) -> *unsafe mut NominalType { |
| 1372 | // Nb. We don't attempt to de-duplicate nominal type entries, |
| 1373 | // since they don't carry node information and we create |
| 1374 | // placeholder entries when binding symbols. |
| 1375 | let entry = try! alloc::allocRaw( |
| 1376 | self.arena, @sizeOf(NominalType), @alignOf(NominalType) |
| 1377 | ) as *unsafe mut NominalType; |
| 1378 | |
| 1379 | set *entry = info; |
| 1380 | |
| 1381 | return entry; |
| 1382 | } |
| 1383 | |
| 1384 | /// Allocate the single runtime layout for a nominal declaration. |
| 1385 | unsafe fn allocLayout 'arena (self: &mut Resolver 'arena, value: Layout) -> *Layout { |
| 1386 | let layout = try! alloc::alloc(&mut *self.arena, @sizeOf(Layout), @alignOf(Layout)) as *mut Layout; |
| 1387 | set *layout = value; |
| 1388 | return layout; |
| 1389 | } |
| 1390 | |
| 1391 | /// Get the exact arguments of an applied nominal descriptor. |
| 1392 | export fn nominalApplication(info: &NominalType) -> ?*unsafe mut NominalApplication { |
| 1393 | match *info { |
| 1394 | case NominalType::Application(applied) => return applied, |
| 1395 | case NominalType::Record(body) => return body.application, |
| 1396 | case NominalType::Union(body) => return body.application, |
| 1397 | else => return nil, |
| 1398 | } |
| 1399 | } |
| 1400 | |
| 1401 | /// Return the private visit bit for one compile-time region role. |
| 1402 | fn regionTypeRoleBit(role: RegionTypeRole) -> u8 { |
| 1403 | match role { |
| 1404 | case RegionTypeRole::CellPermission => return 1, |
| 1405 | case RegionTypeRole::Reference => return 2, |
| 1406 | case RegionTypeRole::Retention => return 4, |
| 1407 | case RegionTypeRole::StorageValidation => return 8, |
| 1408 | case RegionTypeRole::StoragePresence => return 16, |
| 1409 | } |
| 1410 | } |
| 1411 | |
| 1412 | /// Start a cycle-safe nominal traversal without allocating per-call scratch. |
| 1413 | unsafe fn nextNominalTraversalGeneration 'arena (self: &mut Resolver 'arena) -> u32 { |
| 1414 | if self.nominalTraversalGeneration == parser::U32_MAX { |
| 1415 | let mut cursor = self.applications; |
| 1416 | while let applied = cursor { |
| 1417 | set applied.traversalGeneration = 0; |
| 1418 | set applied.traversalRoles = 0; |
| 1419 | set cursor = applied.next; |
| 1420 | } |
| 1421 | set self.nominalTraversalGeneration = 0; |
| 1422 | } |
| 1423 | set self.nominalTraversalGeneration += 1; |
| 1424 | return self.nominalTraversalGeneration; |
| 1425 | } |
| 1426 | |
| 1427 | /// Mark an applied nominal visited once for a role in one traversal generation. |
| 1428 | unsafe fn visitNominalApplication( |
| 1429 | applied: *unsafe mut NominalApplication, |
| 1430 | generation: u32, |
| 1431 | role: RegionTypeRole, |
| 1432 | ) -> bool { |
| 1433 | if applied.traversalGeneration <> generation { |
| 1434 | set applied.traversalGeneration = generation; |
| 1435 | set applied.traversalRoles = 0; |
| 1436 | } |
| 1437 | let bit = regionTypeRoleBit(role); |
| 1438 | if (applied.traversalRoles & bit) <> 0 { |
| 1439 | return false; |
| 1440 | } |
| 1441 | set applied.traversalRoles |= bit; |
| 1442 | return true; |
| 1443 | } |
| 1444 | |
| 1445 | /// Get source region parameters without forcing a recursive type's layout. |
| 1446 | unsafe fn nominalParameters 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType) -> ?*RegionScope |
| 1447 | throws (ResolveError) |
| 1448 | { |
| 1449 | match *info { |
| 1450 | case NominalType::Placeholder(node) => return try declarationRegions(self, node), |
| 1451 | case NominalType::Resolving(node) => return try declarationRegions(self, node), |
| 1452 | case NominalType::Application(applied) => return applied.parameters, |
| 1453 | case NominalType::Record(body) => return body.regions, |
| 1454 | case NominalType::Union(body) => return body.regions, |
| 1455 | } |
| 1456 | } |
| 1457 | |
| 1458 | /// Bind the regions declared by a nominal source node. |
| 1459 | unsafe fn declarationRegions 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*RegionScope |
| 1460 | throws (ResolveError) |
| 1461 | { |
| 1462 | match node.value { |
| 1463 | case ast::NodeValue::RecordDecl(decl) => return try bindRegions(self, node, decl.regions), |
| 1464 | case ast::NodeValue::UnionDecl(decl) => return try bindRegions(self, node, decl.regions), |
| 1465 | else => panic "declarationRegions: expected nominal declaration", |
| 1466 | } |
| 1467 | } |
| 1468 | |
| 1469 | /// Use a hinted application only for the same unapplied nominal declaration. |
| 1470 | unsafe fn hintedNominal(info: *unsafe NominalType, hint: Type) -> *unsafe NominalType { |
| 1471 | if nominalApplication(info) <> nil { |
| 1472 | return info; |
| 1473 | } |
| 1474 | let mut target = hint; |
| 1475 | if let case Type::Optional(inner) = target { |
| 1476 | set target = *inner; |
| 1477 | } |
| 1478 | if let case Type::Nominal(other) = target { |
| 1479 | if let applied = nominalApplication(other); applied.base == info { |
| 1480 | return other; |
| 1481 | } |
| 1482 | } |
| 1483 | return info; |
| 1484 | } |
| 1485 | |
| 1486 | /// Require explicit arguments for a parameterized nominal type. |
| 1487 | unsafe fn requireNominalArguments 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType, site: *ast::Node) |
| 1488 | throws (ResolveError) |
| 1489 | { |
| 1490 | if nominalApplication(info) <> nil { |
| 1491 | return; |
| 1492 | } |
| 1493 | if let parameters = try nominalParameters(self, info) { |
| 1494 | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { |
| 1495 | expected: parameters.entries.len, actual: 0, |
| 1496 | })); |
| 1497 | } |
| 1498 | } |
| 1499 | |
| 1500 | /// Intern an exact nominal application before resolving its recursive members. |
| 1501 | unsafe fn internNominalApplication 'arena ( |
| 1502 | self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution |
| 1503 | ) -> *unsafe mut NominalType { |
| 1504 | let mut hash = addressHash(base as u64); |
| 1505 | for argument in map.arguments { |
| 1506 | let region = argument else panic "internNominalApplication: incomplete map"; |
| 1507 | set hash = (hash ^ region.id) * CACHE_HASH_PRIME; |
| 1508 | } |
| 1509 | let bucket = hash & (APPLICATION_BUCKETS - 1); |
| 1510 | let mut cursor = self.applicationBuckets[bucket]; |
| 1511 | while let applied = cursor { |
| 1512 | if applied.base == base { |
| 1513 | let mut same = true; |
| 1514 | for argument, i in applied.arguments { |
| 1515 | let other = map.arguments[i] else panic "internNominalApplication: missing argument"; |
| 1516 | if argument.id <> other.id { |
| 1517 | set same = false; |
| 1518 | break; |
| 1519 | } |
| 1520 | } |
| 1521 | if same { |
| 1522 | return applied.view; |
| 1523 | } |
| 1524 | } |
| 1525 | set cursor = applied.bucketNext; |
| 1526 | } |
| 1527 | let arguments = try! alloc::allocRawSlice( |
| 1528 | self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len |
| 1529 | ) as *unsafe mut [*unsafe types::Region]; |
| 1530 | for argument, i in map.arguments { |
| 1531 | let region = argument else panic "internNominalApplication: incomplete map"; |
| 1532 | set arguments[i] = region; |
| 1533 | } |
| 1534 | let entry = try! alloc::allocRaw( |
| 1535 | self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication) |
| 1536 | ) as *unsafe mut NominalApplication; |
| 1537 | let view = allocNominalType(self, NominalType::Application(entry)); |
| 1538 | set *entry = NominalApplication { |
| 1539 | base, parameters: map.parameters, arguments, view, |
| 1540 | traversalGeneration: 0, traversalRoles: 0, |
| 1541 | next: self.applications, bucketNext: self.applicationBuckets[bucket], |
| 1542 | }; |
| 1543 | set self.applications = entry; |
| 1544 | set self.applicationBuckets[bucket] = entry; |
| 1545 | return view; |
| 1546 | } |
| 1547 | |
| 1548 | /// Check explicit region arguments and intern the applied nominal type. |
| 1549 | unsafe fn applyNominalRegions 'arena ( |
| 1550 | self: &mut Resolver 'arena, base: *unsafe NominalType, regions: *[*ast::Node], site: *ast::Node |
| 1551 | ) -> *unsafe mut NominalType throws (ResolveError) { |
| 1552 | if nominalApplication(base) <> nil { |
| 1553 | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: 0, actual: regions.len })); |
| 1554 | } |
| 1555 | let parameters = try nominalParameters(self, base); |
| 1556 | let mut count: u32 = 0; |
| 1557 | if let scope = parameters { |
| 1558 | set count = scope.entries.len; |
| 1559 | } |
| 1560 | if count <> regions.len { |
| 1561 | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len })); |
| 1562 | } |
| 1563 | let scope = parameters else panic "applyNominalRegions: empty application"; |
| 1564 | let map = regionSubstitution(self, scope); |
| 1565 | for region, i in regions { |
| 1566 | set map.arguments[i] = try resolveRegion(self, region); |
| 1567 | } |
| 1568 | try validateRegionArguments(self, &map, nil, nil, site); |
| 1569 | return internNominalApplication(self, base, &map); |
| 1570 | } |
| 1571 | |
| 1572 | /// Complete nominal views stored inline within an applied type. |
| 1573 | /// Pointer, slice, and cell targets have independent storage layouts. |
| 1574 | unsafe fn resolveInlineTypeViews 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) |
| 1575 | throws (ResolveError) |
| 1576 | { |
| 1577 | match ty { |
| 1578 | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
| 1579 | case Type::Array(array) => try resolveInlineTypeViews(self, *array.item, site), |
| 1580 | case Type::Optional(inner) => try resolveInlineTypeViews(self, *inner, site), |
| 1581 | else => { |
| 1582 | }, |
| 1583 | } |
| 1584 | } |
| 1585 | |
| 1586 | /// Resolve a substituted member view with the source declaration's shared layout. |
| 1587 | unsafe fn resolveNominalApplication 'arena (self: &mut Resolver 'arena, applied: *unsafe mut NominalApplication, site: *ast::Node) |
| 1588 | throws (ResolveError) |
| 1589 | { |
| 1590 | try ensureNominalResolved(self, applied.base, site); |
| 1591 | let map = regionSubstitution(self, applied.parameters); |
| 1592 | for argument, i in applied.arguments { |
| 1593 | set map.arguments[i] = argument; |
| 1594 | } |
| 1595 | let allocator = alloc::arenaAllocator(self.arena); |
| 1596 | match *applied.base { |
| 1597 | case NominalType::Record(body) => { |
| 1598 | let mut fields: *mut [RecordField] = &mut []; |
| 1599 | for field in body.fields { |
| 1600 | let fieldType = substituteRegions(self, &map, field.fieldType); |
| 1601 | try resolveInlineTypeViews(self, fieldType, site); |
| 1602 | fields.append(RecordField { |
| 1603 | name: field.name, |
| 1604 | fieldType, |
| 1605 | offset: field.offset, |
| 1606 | }, allocator); |
| 1607 | } |
| 1608 | set *applied.view = NominalType::Record(RecordType { |
| 1609 | privateModule: body.privateModule, |
| 1610 | regions: body.regions, |
| 1611 | application: applied, |
| 1612 | fields: (&fields[..]) as *unsafe [RecordField], |
| 1613 | labeled: body.labeled, |
| 1614 | layout: body.layout, |
| 1615 | declaredLinear: body.declaredLinear, |
| 1616 | declaredCopy: body.declaredCopy, |
| 1617 | }); |
| 1618 | } |
| 1619 | case NominalType::Union(body) => { |
| 1620 | let mut variants: *mut [UnionVariant] = &mut []; |
| 1621 | for variant in body.variants { |
| 1622 | let valueType = substituteRegions(self, &map, variant.valueType); |
| 1623 | try resolveInlineTypeViews(self, valueType, site); |
| 1624 | variants.append(UnionVariant { |
| 1625 | name: variant.name, |
| 1626 | valueType, |
| 1627 | symbol: variant.symbol, |
| 1628 | }, allocator); |
| 1629 | } |
| 1630 | set *applied.view = NominalType::Union(UnionType { |
| 1631 | regions: body.regions, |
| 1632 | application: applied, |
| 1633 | variants: (&variants[..]) as *unsafe [UnionVariant], |
| 1634 | layout: body.layout, |
| 1635 | valOffset: body.valOffset, |
| 1636 | isAllVoid: body.isAllVoid, |
| 1637 | declaredLinear: body.declaredLinear, |
| 1638 | declaredCopy: body.declaredCopy, |
| 1639 | }); |
| 1640 | } |
| 1641 | else => panic "resolveNominalApplication: unresolved base", |
| 1642 | } |
| 1643 | } |
| 1644 | |
| 1645 | /// Complete all applied member views before semantic metadata reaches lowering. |
| 1646 | unsafe fn resolveNominalApplications 'arena (self: &mut Resolver 'arena, site: *ast::Node) throws (ResolveError) { |
| 1647 | let mut end = self.completedApplications; |
| 1648 | loop { |
| 1649 | let first = self.applications; |
| 1650 | let mut cursor = first; |
| 1651 | while cursor <> end { |
| 1652 | let applied = cursor else panic "resolveNominalApplications: invalid frontier"; |
| 1653 | try ensureNominalResolved(self, applied.view, site); |
| 1654 | set cursor = applied.next; |
| 1655 | } |
| 1656 | if self.applications == first { |
| 1657 | set self.completedApplications = first; |
| 1658 | break; |
| 1659 | } |
| 1660 | set end = first; |
| 1661 | } |
| 1662 | let previousRegions = self.regionScope; |
| 1663 | let previousModule = self.currentMod; |
| 1664 | let mut index: u32 = 0; |
| 1665 | while index < self.cellChecks.len { |
| 1666 | let check = self.cellChecks[index]; |
| 1667 | set self.regionScope = check.regions; |
| 1668 | set self.currentMod = check.moduleId; |
| 1669 | try validateCellPayload(self, check.node, check.payload, check.permission) catch error { |
| 1670 | set self.regionScope = previousRegions; |
| 1671 | set self.currentMod = previousModule; |
| 1672 | throw error; |
| 1673 | }; |
| 1674 | set index += 1; |
| 1675 | } |
| 1676 | set self.cellChecks.len = 0; |
| 1677 | set self.regionScope = previousRegions; |
| 1678 | set self.currentMod = previousModule; |
| 1679 | } |
| 1680 | |
| 1681 | |
| 1682 | /// Allocate a function type descriptor and return a pointer to it. |
| 1683 | unsafe fn allocFnType 'arena (self: &mut Resolver 'arena, info: FnType) -> *FnType { |
| 1684 | let entry = try! alloc::alloc( |
| 1685 | &mut *self.arena, @sizeOf(FnType), @alignOf(FnType) |
| 1686 | ) as *mut FnType; |
| 1687 | |
| 1688 | set *entry = info; |
| 1689 | |
| 1690 | return entry; |
| 1691 | } |
| 1692 | |
| 1693 | /// Returns an error, if any, associated with the given node. |
| 1694 | fn errorForNode 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Error { |
| 1695 | for i in 0..self.errors.len { |
| 1696 | let err = self.errors.entries[i]; |
| 1697 | if err.node == node { |
| 1698 | return err; |
| 1699 | } |
| 1700 | } |
| 1701 | return nil; |
| 1702 | } |
| 1703 | |
| 1704 | /// Storage buffers used by the analyzer. |
| 1705 | export record ResolverStorage { |
| 1706 | /// Node semantic metadata indexed by node ID. |
| 1707 | nodeData: *mut [NodeData], |
| 1708 | /// Package scope. |
| 1709 | pkgScope: *unsafe mut Scope, |
| 1710 | /// Error storage. |
| 1711 | errors: *mut [Error], |
| 1712 | } |
| 1713 | |
| 1714 | /// Input for resolving a single package. |
| 1715 | export record Pkg: Copy { |
| 1716 | /// Root module entry. |
| 1717 | rootEntry: *module::ModuleEntry, |
| 1718 | /// Root AST node. |
| 1719 | rootAst: *ast::Node, |
| 1720 | } |
| 1721 | |
| 1722 | /// Construct a resolver with module context and backing storage. |
| 1723 | /// The arena owner and backing bytes must retain stable addresses during use. |
| 1724 | /// Arena reclamation can occur only after all metadata uses end. |
| 1725 | export unsafe fn resolver 'arena ( |
| 1726 | arena: &'arena mut alloc::Arena, |
| 1727 | storage: ResolverStorage, |
| 1728 | config: Config |
| 1729 | ) -> Resolver 'arena { |
| 1730 | let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage"; |
| 1731 | let applicationBuckets = try! alloc::allocRawSlice( |
| 1732 | arena, @sizeOf(?*unsafe mut NominalApplication), @alignOf(?*unsafe mut NominalApplication), APPLICATION_BUCKETS |
| 1733 | ) as *unsafe mut [?*unsafe mut NominalApplication]; |
| 1734 | for i in 0..applicationBuckets.len { |
| 1735 | set applicationBuckets[i] = nil; |
| 1736 | } |
| 1737 | let types = try! alloc::allocRawSlice( |
| 1738 | arena, @sizeOf(?*TypeNode), @alignOf(?*TypeNode), TYPE_BUCKETS |
| 1739 | ) as *unsafe mut [?*TypeNode]; |
| 1740 | for i in 0..types.len { |
| 1741 | set types[i] = nil; |
| 1742 | } |
| 1743 | let symbols = try! alloc::allocRawSlice( |
| 1744 | arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS |
| 1745 | ) as *unsafe mut [*unsafe mut Symbol]; |
| 1746 | |
| 1747 | // Initialize the root scope. |
| 1748 | // TODO: Set this up when declaring `PKG_SCOPE`, not here. |
| 1749 | set *pkgScope = Scope { |
| 1750 | owner: nil, |
| 1751 | parent: nil, |
| 1752 | moduleId: nil, |
| 1753 | symbols, |
| 1754 | symbolsLen: 0, |
| 1755 | }; |
| 1756 | |
| 1757 | // Clear all node semantic metadata to sentinel values. |
| 1758 | // TODO: Use array repeat literal? |
| 1759 | for i in 0..nodeData.len { |
| 1760 | set nodeData[i] = NodeData { |
| 1761 | localCount: 0, |
| 1762 | ty: Type::Unknown, |
| 1763 | coercion: Coercion::Identity, |
| 1764 | binding: nil, |
| 1765 | constValue: nil, |
| 1766 | scope: nil, |
| 1767 | extra: NodeExtra::None, |
| 1768 | }; |
| 1769 | } |
| 1770 | |
| 1771 | let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined; |
| 1772 | // TODO: Simplify. |
| 1773 | for i in 0..moduleScopes.len { |
| 1774 | set moduleScopes[i] = nil; |
| 1775 | } |
| 1776 | return Resolver 'arena { |
| 1777 | symbolCount: 0, |
| 1778 | regionScope: nil, |
| 1779 | applications: nil, |
| 1780 | completedApplications: nil, |
| 1781 | applicationBuckets, |
| 1782 | nominalTraversalGeneration: 0, |
| 1783 | cellChecks: &mut [], |
| 1784 | scope: pkgScope, |
| 1785 | pkgScope: pkgScope, |
| 1786 | loopStack: [LoopCtx { hasBreak: false }; MAX_LOOP_DEPTH], |
| 1787 | loopDepth: 0, |
| 1788 | currentFn: nil, |
| 1789 | currentFnNode: nil, |
| 1790 | currentMod: 0, |
| 1791 | inUnsafeContext: false, |
| 1792 | config, |
| 1793 | arena, |
| 1794 | nodeData: NodeDataTable { entries: nodeData }, |
| 1795 | types, |
| 1796 | errors: DiagnosticBuffer { entries: errors, len: 0 }, |
| 1797 | moduleEntries: [nil; module::MAX_MODULES], |
| 1798 | moduleRoots: [nil; module::MAX_MODULES], |
| 1799 | moduleScopes, |
| 1800 | instances: undefined, |
| 1801 | instancesLen: 0, |
| 1802 | methods: undefined, |
| 1803 | methodsLen: 0, |
| 1804 | }; |
| 1805 | } |
| 1806 | |
| 1807 | /// Capture the current errors in an immutable arena allocation. |
| 1808 | /// The allocation must remain valid while the diagnostics are used. |
| 1809 | export unsafe fn diagnostics 'arena (self: &mut Resolver 'arena) -> Diagnostics { |
| 1810 | let count = self.errors.len; |
| 1811 | let entries = try! alloc::allocSlice( |
| 1812 | self.arena, @sizeOf(Error), @alignOf(Error), count |
| 1813 | ) as *mut [Error]; |
| 1814 | for i in 0..self.errors.len { |
| 1815 | set entries[i] = self.errors.entries[i]; |
| 1816 | } |
| 1817 | return Diagnostics { errors: entries }; |
| 1818 | } |
| 1819 | |
| 1820 | /// Return `true` if there are no errors in the diagnostics. |
| 1821 | export fn success(diag: &Diagnostics) -> bool { |
| 1822 | return diag.errors.len == 0; |
| 1823 | } |
| 1824 | |
| 1825 | /// Retrieve an error diagnostic by index, if present. |
| 1826 | export fn errorAt(errs: &[Error], index: u32) -> ?Error { |
| 1827 | if index >= errs.len { |
| 1828 | return nil; |
| 1829 | } |
| 1830 | return errs[index]; |
| 1831 | } |
| 1832 | |
| 1833 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
| 1834 | fn emitError 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
| 1835 | // If our error list is full, just return an error without recording it. |
| 1836 | if self.errors.len >= self.errors.entries.len { |
| 1837 | return ResolveError::Failure; |
| 1838 | } |
| 1839 | // Don't record more than one error per node. |
| 1840 | if let n = node; errorForNode(self, n) <> nil { |
| 1841 | return ResolveError::Failure; |
| 1842 | } |
| 1843 | let idx = self.errors.len; |
| 1844 | set self.errors.entries[idx] = Error { kind, node, moduleId: self.currentMod }; |
| 1845 | set self.errors.len = idx + 1; |
| 1846 | |
| 1847 | return ResolveError::Failure; |
| 1848 | } |
| 1849 | |
| 1850 | /// Like [`emitError`], but for type mismatches specifically. |
| 1851 | fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
| 1852 | return emitError(self, node, ErrorKind::TypeMismatch(mismatch)); |
| 1853 | } |
| 1854 | |
| 1855 | /// Allocate a scope object with the given symbol capacity. |
| 1856 | unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope { |
| 1857 | // Check for an existing scope for this node, and don't allocate a new |
| 1858 | // one in that case. |
| 1859 | if let scope = scopeFor(self, owner) { |
| 1860 | return scope; |
| 1861 | } |
| 1862 | assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds"; |
| 1863 | let p = try! alloc::allocRaw(self.arena, @sizeOf(Scope), @alignOf(Scope)); |
| 1864 | let entry = p as *unsafe mut Scope; |
| 1865 | |
| 1866 | // Allocate symbols from the arena. |
| 1867 | let symbols = try! alloc::allocRawSlice( |
| 1868 | self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity |
| 1869 | ) as *unsafe mut [*unsafe mut Symbol]; |
| 1870 | |
| 1871 | set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 }; |
| 1872 | set self.nodeData.entries[owner.id].scope = entry; |
| 1873 | |
| 1874 | return entry; |
| 1875 | } |
| 1876 | |
| 1877 | /// Enter a new local scope that is the child of the current scope. |
| 1878 | /// This creates a parent/child relationship that means that lookups in the |
| 1879 | /// child scope can recurse upwards. |
| 1880 | export unsafe fn enterScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node) -> *unsafe Scope { |
| 1881 | let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS); |
| 1882 | set scope.parent = self.scope; |
| 1883 | set self.scope = scope; |
| 1884 | return scope; |
| 1885 | } |
| 1886 | |
| 1887 | /// Enter a module scope. Returns an object that can be used to exit the scope. |
| 1888 | export unsafe fn enterModuleScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
| 1889 | let prevScope = self.scope; |
| 1890 | let prevMod = self.currentMod; |
| 1891 | let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS); |
| 1892 | |
| 1893 | set self.scope = scope; |
| 1894 | set self.scope.moduleId = module.id; |
| 1895 | set self.currentMod = module.id; |
| 1896 | // TODO: Allow any unsigned integer to index an array. |
| 1897 | set self.moduleScopes[module.id as u32] = scope; |
| 1898 | |
| 1899 | return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod }; |
| 1900 | } |
| 1901 | |
| 1902 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
| 1903 | unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
| 1904 | let modEntry = findChildModule(self, name, self.currentMod) |
| 1905 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
| 1906 | let modRoot = self.moduleRoots[modEntry.id as u32] |
| 1907 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
| 1908 | |
| 1909 | return enterModuleScope(self, modRoot, modEntry); |
| 1910 | } |
| 1911 | |
| 1912 | /// Exit a module scope, given the object returned by `enterModuleScope`. |
| 1913 | export fn exitModuleScope 'arena (self: &mut Resolver 'arena, entry: ModuleScope) { |
| 1914 | set self.scope = entry.prevScope; |
| 1915 | set self.currentMod = entry.prevMod; |
| 1916 | } |
| 1917 | |
| 1918 | /// Exit the most recent scope. |
| 1919 | export unsafe fn exitScope 'arena (self: &mut Resolver 'arena) { |
| 1920 | let parent = self.scope.parent else { |
| 1921 | // TODO: This should be a panic, but one of the tests hits this |
| 1922 | // clause, which might be a bug in the generator. |
| 1923 | return; |
| 1924 | }; |
| 1925 | set self.scope = parent; |
| 1926 | } |
| 1927 | |
| 1928 | /// Initialize a loop context before making it active. |
| 1929 | fn enterLoop 'arena (self: &mut Resolver 'arena) { |
| 1930 | assert self.loopDepth < MAX_LOOP_DEPTH, "enterLoop: loop nesting depth exceeded"; |
| 1931 | set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false }; |
| 1932 | set self.loopDepth += 1; |
| 1933 | } |
| 1934 | |
| 1935 | /// End the active loop context and return its control-flow type. |
| 1936 | fn exitLoop 'arena (self: &mut Resolver 'arena) -> Type { |
| 1937 | assert self.loopDepth > 0, "exitLoop: loop depth underflow"; |
| 1938 | // Pop and check if break was encountered. |
| 1939 | set self.loopDepth -= 1; |
| 1940 | if self.loopStack[self.loopDepth].hasBreak { |
| 1941 | return Type::Void; |
| 1942 | } |
| 1943 | return Type::Never; |
| 1944 | } |
| 1945 | |
| 1946 | /// Visit the body of a loop while tracking nesting depth. |
| 1947 | unsafe fn visitLoop 'arena (self: &mut Resolver 'arena, body: *ast::Node) -> Type |
| 1948 | throws (ResolveError) |
| 1949 | { |
| 1950 | enterLoop(self); |
| 1951 | try infer(self, body) catch { |
| 1952 | exitLoop(self); |
| 1953 | throw ResolveError::Failure; |
| 1954 | }; |
| 1955 | return exitLoop(self); |
| 1956 | } |
| 1957 | |
| 1958 | /// Require that loop control statements appear inside a loop. |
| 1959 | /// Record breaks and assign the control statement's diverging type. |
| 1960 | fn resolveLoopControl 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
| 1961 | if self.loopDepth == 0 { |
| 1962 | throw emitError(self, node, ErrorKind::InvalidLoopControl); |
| 1963 | } |
| 1964 | match node.value { |
| 1965 | case ast::NodeValue::Break => { |
| 1966 | // Mark that the current loop has a reachable break. |
| 1967 | set self.loopStack[self.loopDepth - 1].hasBreak = true; |
| 1968 | } |
| 1969 | case ast::NodeValue::Continue => {} |
| 1970 | else => panic "resolveLoopControl: expected loop control statement", |
| 1971 | } |
| 1972 | return setNodeType(self, node, Type::Never); |
| 1973 | } |
| 1974 | |
| 1975 | /// Bind a loop pattern to the provided type. |
| 1976 | unsafe fn bindForLoopPattern 'arena (self: &mut Resolver 'arena, pattern: *ast::Node, ty: Type, mutable: bool) |
| 1977 | throws (ResolveError) |
| 1978 | { |
| 1979 | match pattern.value { |
| 1980 | case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => { |
| 1981 | let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0); |
| 1982 | } |
| 1983 | else => { |
| 1984 | let actualTy = try checkAssignable(self, pattern, ty); |
| 1985 | setNodeType(self, pattern, actualTy); |
| 1986 | } |
| 1987 | } |
| 1988 | } |
| 1989 | |
| 1990 | /// Set the expected return type for a new function body. |
| 1991 | unsafe fn enterFn 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: &FnType) { |
| 1992 | assert self.currentFn == nil, "enterFn: already in a function"; |
| 1993 | set self.currentFn = *ty; |
| 1994 | set self.currentFnNode = node; |
| 1995 | enterScope(self, node); |
| 1996 | } |
| 1997 | |
| 1998 | /// Clear the expected return type when leaving a function body. |
| 1999 | unsafe fn exitFn 'arena (self: &mut Resolver 'arena) { |
| 2000 | if self.currentFn == nil { |
| 2001 | // TODO: This should be a panic, but one of the tests hits this |
| 2002 | // clause, which might be a bug in the generator. |
| 2003 | return; |
| 2004 | } |
| 2005 | set self.currentFn = nil; |
| 2006 | set self.currentFnNode = nil; |
| 2007 | exitScope(self); |
| 2008 | } |
| 2009 | |
| 2010 | /// Extract the identifier text from a node. |
| 2011 | fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8] |
| 2012 | throws (ResolveError) |
| 2013 | { |
| 2014 | let case ast::NodeValue::Ident(name) = node.value |
| 2015 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
| 2016 | return name; |
| 2017 | } |
| 2018 | |
| 2019 | /// Associate a resolved symbol with an AST node. |
| 2020 | unsafe fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) { |
| 2021 | if let existingSym = self.nodeData.entries[node.id].binding { |
| 2022 | panic "setNodeSymbol: a symbol is already associated with this node"; |
| 2023 | } |
| 2024 | set self.nodeData.entries[node.id].binding = ResolvedSymbol { id: symbol.id, symbol }; |
| 2025 | } |
| 2026 | |
| 2027 | /// Associate a resolved type with an AST node and return it. |
| 2028 | fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type { |
| 2029 | if ty == Type::Unknown { |
| 2030 | // In this case, we simply don't associate a type. |
| 2031 | return ty; |
| 2032 | } |
| 2033 | set self.nodeData.entries[node.id].ty = ty; |
| 2034 | |
| 2035 | return ty; |
| 2036 | } |
| 2037 | |
| 2038 | /// Unify the types of two branches for control flow. Returns `never` only if |
| 2039 | /// both branches diverge, otherwise returns `void`. If the else branch is |
| 2040 | /// absent, we assume it doesn't diverge. |
| 2041 | fn unifyBranches(left: Type, right: ?Type) -> Type { |
| 2042 | if left == Type::Never { |
| 2043 | if let ty = right; ty == Type::Never { |
| 2044 | return Type::Never; |
| 2045 | } |
| 2046 | } |
| 2047 | return Type::Void; |
| 2048 | } |
| 2049 | |
| 2050 | /// Associate a coercion plan with an AST node. |
| 2051 | fn setNodeCoercion 'arena (self: &mut Resolver 'arena, node: *ast::Node, coercion: Coercion) -> Coercion { |
| 2052 | if coercion == Coercion::Identity { |
| 2053 | return coercion; |
| 2054 | } |
| 2055 | set self.nodeData.entries[node.id].coercion = coercion; |
| 2056 | |
| 2057 | return coercion; |
| 2058 | } |
| 2059 | |
| 2060 | /// Associate a constant value with an AST node. |
| 2061 | fn setNodeConstValue 'arena (self: &mut Resolver 'arena, node: *ast::Node, value: ConstValue) { |
| 2062 | set self.nodeData.entries[node.id].constValue = value; |
| 2063 | } |
| 2064 | |
| 2065 | /// Associate a record field index with a record literal field node. |
| 2066 | fn setRecordFieldIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node, index: u32) { |
| 2067 | set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index }; |
| 2068 | } |
| 2069 | |
| 2070 | /// Associate slice range metadata with a subscript expression. |
| 2071 | fn setSliceRangeInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: SliceRangeInfo) { |
| 2072 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info); |
| 2073 | } |
| 2074 | |
| 2075 | /// Associate union variant metadata with a pattern or constructor node. |
| 2076 | fn setVariantInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, ordinal: u32, tag: u32) { |
| 2077 | set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag }; |
| 2078 | } |
| 2079 | |
| 2080 | /// Associate trait method call metadata with a call node. |
| 2081 | fn setTraitMethodCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) { |
| 2082 | set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex }; |
| 2083 | } |
| 2084 | |
| 2085 | /// Associate for-loop metadata with a for-loop node. |
| 2086 | fn setForLoopInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: ForLoopInfo) { |
| 2087 | set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info); |
| 2088 | } |
| 2089 | |
| 2090 | /// Retrieve the constant value associated with a node, if any. |
| 2091 | export fn constValueEntry 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ConstValue { |
| 2092 | return self.nodeData.entries[node.id].constValue; |
| 2093 | } |
| 2094 | |
| 2095 | /// Get the resolved record field index for a record literal field node. |
| 2096 | export fn recordFieldIndexFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?u32 { |
| 2097 | if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra { |
| 2098 | return index; |
| 2099 | } |
| 2100 | return nil; |
| 2101 | } |
| 2102 | |
| 2103 | /// Get the range metadata for a slice borrow or range assignment. |
| 2104 | export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo { |
| 2105 | if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra { |
| 2106 | return info; |
| 2107 | } |
| 2108 | return nil; |
| 2109 | } |
| 2110 | |
| 2111 | /// Get the for-loop metadata for a for-loop node. |
| 2112 | export fn forLoopInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ForLoopInfo { |
| 2113 | if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra { |
| 2114 | return info; |
| 2115 | } |
| 2116 | return nil; |
| 2117 | } |
| 2118 | |
| 2119 | /// Associate match prong metadata with a match prong node. |
| 2120 | fn setProngCatchAll 'arena (self: &mut Resolver 'arena, node: *ast::Node, catchAll: bool) { |
| 2121 | set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll }; |
| 2122 | } |
| 2123 | |
| 2124 | /// Check if a prong is catch-all. |
| 2125 | export fn isProngCatchAll 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
| 2126 | if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra { |
| 2127 | return catchAll; |
| 2128 | } |
| 2129 | return false; |
| 2130 | } |
| 2131 | |
| 2132 | /// Set match metadata. |
| 2133 | fn setMatchConst 'arena (self: &mut Resolver 'arena, node: *ast::Node, isConst: bool) { |
| 2134 | set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst }; |
| 2135 | } |
| 2136 | |
| 2137 | /// Check if a match has all constant patterns. |
| 2138 | export fn isMatchConst 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
| 2139 | if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra { |
| 2140 | return isConst; |
| 2141 | } |
| 2142 | return false; |
| 2143 | } |
| 2144 | |
| 2145 | /// Get the resolver metadata for a node. |
| 2146 | export fn nodeData 'arena (self: &Resolver 'arena, node: *ast::Node) -> NodeData { |
| 2147 | return self.nodeData.entries[node.id]; |
| 2148 | } |
| 2149 | |
| 2150 | /// Get the type for a node, or `nil` if unknown. |
| 2151 | export fn typeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Type { |
| 2152 | let ty = self.nodeData.entries[node.id].ty; |
| 2153 | if ty == Type::Unknown { |
| 2154 | return nil; |
| 2155 | } |
| 2156 | return ty; |
| 2157 | } |
| 2158 | |
| 2159 | /// Get the scope associated with a node. |
| 2160 | export fn scopeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope { |
| 2161 | return self.nodeData.entries[node.id].scope; |
| 2162 | } |
| 2163 | |
| 2164 | /// Get the symbol bound to a node. |
| 2165 | export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol { |
| 2166 | let binding = self.nodeData.entries[node.id].binding else return nil; |
| 2167 | return binding.symbol; |
| 2168 | } |
| 2169 | |
| 2170 | /// Get the coercion plan associated with a node, if any. |
| 2171 | export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion { |
| 2172 | let c = self.nodeData.entries[node.id].coercion; |
| 2173 | if c == Coercion::Identity { |
| 2174 | return nil; |
| 2175 | } |
| 2176 | return c; |
| 2177 | } |
| 2178 | |
| 2179 | /// Get the module ID for a symbol by walking up its scope chain. |
| 2180 | export unsafe fn moduleIdForSymbol 'arena (self: &Resolver 'arena, sym: *unsafe Symbol) -> ?u16 { |
| 2181 | // For module-level symbols, return the cached module ID. |
| 2182 | if let id = sym.moduleId { |
| 2183 | return id; |
| 2184 | } |
| 2185 | // For module symbols, return the module ID directly. |
| 2186 | if let case SymbolData::Module { entry, .. } = sym.data { |
| 2187 | return entry.id; |
| 2188 | } |
| 2189 | // If this node has its own scope (functions, types, etc.), walk up from there. |
| 2190 | if let scope = self.nodeData.entries[sym.node.id].scope { |
| 2191 | return findModuleForScope(scope); |
| 2192 | } |
| 2193 | return nil; |
| 2194 | } |
| 2195 | |
| 2196 | /// Get the binding node for a variant pattern. |
| 2197 | /// Returns the argument node if this is a variant constructor with a non-placeholder binding. |
| 2198 | export unsafe fn variantPatternBinding 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> ?*ast::Node { |
| 2199 | let case ast::NodeValue::Call(call) = pattern.value |
| 2200 | else return nil; |
| 2201 | let sym = symbolFor(self, call.callee) |
| 2202 | else return nil; |
| 2203 | let case SymbolData::Variant { .. } = sym.data |
| 2204 | else return nil; |
| 2205 | |
| 2206 | if call.args.len == 0 { |
| 2207 | return nil; |
| 2208 | } |
| 2209 | let arg = call.args[0]; |
| 2210 | |
| 2211 | if let case ast::NodeValue::Placeholder = arg.value { |
| 2212 | return nil; |
| 2213 | } |
| 2214 | return arg; |
| 2215 | } |
| 2216 | |
| 2217 | /// Allocate a new symbol, and return a reference to it. |
| 2218 | unsafe fn allocSymbol 'arena (self: &mut Resolver 'arena, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol { |
| 2219 | let sym = try! alloc::allocRaw(self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol; |
| 2220 | assert self.symbolCount < parser::U32_MAX, "allocSymbol: symbol identity overflow"; |
| 2221 | let id = self.symbolCount; |
| 2222 | set self.symbolCount += 1; |
| 2223 | set *sym = Symbol { id, name, data, attrs, node, moduleId: nil }; |
| 2224 | |
| 2225 | return sym; |
| 2226 | } |
| 2227 | |
| 2228 | /// Check that a type is boolean, otherwise throw an error. |
| 2229 | unsafe fn checkBoolean 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
| 2230 | return try checkEqual(self, node, Type::Bool); |
| 2231 | } |
| 2232 | |
| 2233 | /// Check that a type is numeric, otherwise throw an error. |
| 2234 | unsafe fn checkNumeric 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
| 2235 | let ty = try infer(self, node); |
| 2236 | if not isNumericType(ty) { |
| 2237 | throw emitError(self, node, ErrorKind::ExpectedNumeric); |
| 2238 | } |
| 2239 | return ty; |
| 2240 | } |
| 2241 | |
| 2242 | /// Check if a type is a numeric type. |
| 2243 | fn isNumericType(ty: Type) -> bool { |
| 2244 | match ty { |
| 2245 | case Type::U8, Type::U16, Type::U32, Type::U64, |
| 2246 | Type::I8, Type::I16, Type::I32, Type::I64, |
| 2247 | Type::Int => return true, |
| 2248 | else => return false, |
| 2249 | } |
| 2250 | } |
| 2251 | |
| 2252 | /// Check if a type is an unsigned integer type. |
| 2253 | export fn isUnsignedIntegerType(ty: Type) -> bool { |
| 2254 | match ty { |
| 2255 | case Type::U8, Type::U16, Type::U32, Type::U64 => return true, |
| 2256 | else => return false, |
| 2257 | } |
| 2258 | } |
| 2259 | |
| 2260 | /// Return the maximum of two u32 values. |
| 2261 | fn max(a: u32, b: u32) -> u32 { |
| 2262 | if a > b { |
| 2263 | return a; |
| 2264 | } |
| 2265 | return b; |
| 2266 | } |
| 2267 | |
| 2268 | /// Get the layout of a type. |
| 2269 | export unsafe fn getTypeLayout(ty: Type) -> Layout { |
| 2270 | return typeLayout(ty); |
| 2271 | } |
| 2272 | |
| 2273 | /// Traverse owned type links and compute array, optional, or fixed layouts. |
| 2274 | fn typeLayout(ty: Type) -> Layout { |
| 2275 | match ty { |
| 2276 | case Type::Array(arr) => return getArrayLayout(typeLayout(*arr.item), arr.length), |
| 2277 | case Type::Optional(inner) => { |
| 2278 | // Nullable types use null pointer optimization -- no tag byte needed. |
| 2279 | if isNullableType(*inner) { |
| 2280 | return typeLayout(*inner); |
| 2281 | } |
| 2282 | return getOptionalAggregateLayout(typeLayout(*inner)); |
| 2283 | } |
| 2284 | case Type::Nominal(info) => { |
| 2285 | unsafe { |
| 2286 | return getNominalLayout(*info); |
| 2287 | } |
| 2288 | }, |
| 2289 | else => return fixedTypeLayout(ty), |
| 2290 | } |
| 2291 | } |
| 2292 | |
| 2293 | /// Get a layout that does not depend on nested type or nominal metadata. |
| 2294 | fn fixedTypeLayout(ty: Type) -> Layout { |
| 2295 | match ty { |
| 2296 | case Type::Pointer { .. } => return Layout { |
| 2297 | size: PTR_SIZE, alignment: PTR_SIZE |
| 2298 | }, |
| 2299 | case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) => |
| 2300 | return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE }, |
| 2301 | case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 }, |
| 2302 | case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 }, |
| 2303 | case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 }, |
| 2304 | case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 }, |
| 2305 | case Type::Int => return Layout { size: 8, alignment: 8 }, |
| 2306 | case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 }, |
| 2307 | case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
| 2308 | case Type::Cell { .. } => return Layout { |
| 2309 | size: PTR_SIZE, alignment: PTR_SIZE |
| 2310 | }, |
| 2311 | else => { |
| 2312 | panic "fixedTypeLayout: the given type has no fixed layout"; |
| 2313 | } |
| 2314 | } |
| 2315 | } |
| 2316 | |
| 2317 | /// Get the layout of a type or value. |
| 2318 | export unsafe fn getLayout 'arena (self: &Resolver 'arena, node: *ast::Node, ty: Type) -> Layout { |
| 2319 | let mut layout = getTypeLayout(ty); |
| 2320 | // Check for symbol-specific alignment override. |
| 2321 | if let sym = symbolFor(self, node) { |
| 2322 | if let case SymbolData::Value { alignment, .. } = sym.data { |
| 2323 | if alignment > 0 { |
| 2324 | set layout.alignment = alignment; |
| 2325 | } |
| 2326 | } |
| 2327 | } |
| 2328 | return layout; |
| 2329 | } |
| 2330 | |
| 2331 | /// Get an array layout from its element layout and length. |
| 2332 | export fn getArrayLayout(item: Layout, length: u32) -> Layout { |
| 2333 | return Layout { |
| 2334 | size: item.size * length, |
| 2335 | alignment: item.alignment, |
| 2336 | }; |
| 2337 | } |
| 2338 | |
| 2339 | /// Get an optional aggregate layout from its payload layout. |
| 2340 | export fn getOptionalAggregateLayout(innerLayout: Layout) -> Layout { |
| 2341 | let valOffset = getOptionalValOffset(innerLayout); |
| 2342 | let alignment = max(innerLayout.alignment, 1); |
| 2343 | |
| 2344 | return Layout { |
| 2345 | size: mem::alignUp(valOffset + innerLayout.size, alignment), |
| 2346 | alignment, |
| 2347 | }; |
| 2348 | } |
| 2349 | |
| 2350 | /// Get the payload offset within an optional aggregate. |
| 2351 | export fn getOptionalValOffset(inner: Layout) -> u32 { |
| 2352 | return mem::alignUp(1, inner.alignment); |
| 2353 | } |
| 2354 | |
| 2355 | /// Check if a type is optional. |
| 2356 | export fn isOptionalType(ty: Type) -> bool { |
| 2357 | match ty { |
| 2358 | case Type::Optional(_) => return true, |
| 2359 | else => return false, |
| 2360 | } |
| 2361 | } |
| 2362 | |
| 2363 | /// Check if a type uses null pointer optimization. |
| 2364 | /// This applies to optional pointers `?*T` and optional slices `?*[T]`, |
| 2365 | /// where `nil` is represented as a null data pointer with no tag byte. |
| 2366 | export fn isOptionalPointer(ty: Type) -> bool { |
| 2367 | if let case Type::Optional(inner) = ty { |
| 2368 | return isNullableType(*inner); |
| 2369 | } |
| 2370 | return false; |
| 2371 | } |
| 2372 | |
| 2373 | /// Check if a type uses the optional aggregate representation. |
| 2374 | export fn isOptionalAggregate(ty: Type) -> bool { |
| 2375 | if let case Type::Optional(inner) = ty { |
| 2376 | return not isNullableType(*inner); |
| 2377 | } |
| 2378 | return false; |
| 2379 | } |
| 2380 | |
| 2381 | /// Check if a type can use null to represent `nil`. |
| 2382 | /// Pointers and slices have a data pointer that is never null when valid. |
| 2383 | export fn isNullableType(ty: Type) -> bool { |
| 2384 | match ty { |
| 2385 | case Type::Pointer { .. }, Type::Slice { .. } => return true, |
| 2386 | else => return false, |
| 2387 | } |
| 2388 | } |
| 2389 | |
| 2390 | /// Get the layout of a nominal type. |
| 2391 | export fn getNominalLayout(info: NominalType) -> Layout { |
| 2392 | match info { |
| 2393 | case NominalType::Placeholder(_), NominalType::Resolving(_), NominalType::Application(_) => { |
| 2394 | panic "getNominalLayout: unresolved type"; |
| 2395 | } |
| 2396 | case NominalType::Record(recordType) => { |
| 2397 | return *recordType.layout; |
| 2398 | } |
| 2399 | case NominalType::Union(unionType) => { |
| 2400 | return *unionType.layout; |
| 2401 | } |
| 2402 | } |
| 2403 | } |
| 2404 | |
| 2405 | /// Get the layout of a result aggregate with a tag and the larger payload. |
| 2406 | export unsafe fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
| 2407 | return resultLayout(payload, throwList); |
| 2408 | } |
| 2409 | |
| 2410 | /// Compute tagged result storage while borrowing its error type table. |
| 2411 | fn resultLayout(payload: Type, throwList: &[*Type]) -> Layout { |
| 2412 | let payloadLayout = typeLayout(payload); |
| 2413 | let mut maxSize = payloadLayout.size; |
| 2414 | let mut maxAlign = payloadLayout.alignment; |
| 2415 | |
| 2416 | for errType in throwList { |
| 2417 | let errLayout = typeLayout(*errType); |
| 2418 | set maxSize = max(maxSize, errLayout.size); |
| 2419 | set maxAlign = max(maxAlign, errLayout.alignment); |
| 2420 | } |
| 2421 | return Layout { |
| 2422 | size: PTR_SIZE + maxSize, |
| 2423 | alignment: max(PTR_SIZE, maxAlign), |
| 2424 | }; |
| 2425 | } |
| 2426 | |
| 2427 | /// Compute the layout for a union given its resolved variants. |
| 2428 | fn computeUnionLayout(variants: &[UnionVariant]) -> UnionLayoutInfo { |
| 2429 | let tagSize: u32 = 1; |
| 2430 | let mut maxVarSize: u32 = 0; |
| 2431 | let mut maxVarAlign: u32 = 1; |
| 2432 | let mut isAllVoid: bool = true; |
| 2433 | |
| 2434 | for variant in variants { |
| 2435 | if variant.valueType <> Type::Void { |
| 2436 | set isAllVoid = false; |
| 2437 | let payloadLayout = typeLayout(variant.valueType); |
| 2438 | set maxVarSize = max(maxVarSize, payloadLayout.size); |
| 2439 | set maxVarAlign = max(maxVarAlign, payloadLayout.alignment); |
| 2440 | } |
| 2441 | } |
| 2442 | let unionAlignment: u32 = max(1, maxVarAlign); |
| 2443 | let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign); |
| 2444 | let unionLayout = Layout { |
| 2445 | size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment), |
| 2446 | alignment: unionAlignment, |
| 2447 | }; |
| 2448 | return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid }; |
| 2449 | } |
| 2450 | |
| 2451 | /// Compute the discriminant tag for a variant, advancing the iota counter. |
| 2452 | /// If the variant has an explicit `= N` value, uses that; otherwise uses iota. |
| 2453 | fn variantTag(variantDecl: ast::UnionDeclVariant, iota: &mut u32) -> u32 { |
| 2454 | let mut tag: u32 = *iota; |
| 2455 | if let valueNode = variantDecl.value { |
| 2456 | let case ast::NodeValue::Number(lit) = valueNode.value |
| 2457 | else panic "variantTag: expected number literal"; |
| 2458 | set tag = lit.magnitude as u32; |
| 2459 | } |
| 2460 | set *iota = tag + 1; |
| 2461 | return tag; |
| 2462 | } |
| 2463 | |
| 2464 | /// Check if a type is a union without payloads. |
| 2465 | export unsafe fn isVoidUnion(ty: Type) -> bool { |
| 2466 | let case Type::Nominal(NominalType::Union(unionType)) = ty |
| 2467 | else return false; |
| 2468 | return unionType.isAllVoid; |
| 2469 | } |
| 2470 | |
| 2471 | /// Check if a type should be treated as an address-like value. |
| 2472 | fn isAddressType(ty: Type) -> bool { |
| 2473 | if isNullableType(ty) { |
| 2474 | return true; |
| 2475 | } |
| 2476 | match ty { |
| 2477 | case Type::Fn(_) => return true, |
| 2478 | else => return false, |
| 2479 | } |
| 2480 | } |
| 2481 | |
| 2482 | /// Return the representable range for an integer type. |
| 2483 | fn integerRange(ty: Type) -> ?IntegerRange { |
| 2484 | match ty { |
| 2485 | case Type::I8 => return IntegerRange::Signed { |
| 2486 | bits: 8, |
| 2487 | min: I8_MIN as i64, |
| 2488 | max: I8_MAX as i64, |
| 2489 | lim: (I8_MAX as u64) + 1, |
| 2490 | }, |
| 2491 | case Type::I16 => return IntegerRange::Signed { |
| 2492 | bits: 16, |
| 2493 | min: I16_MIN as i64, |
| 2494 | max: I16_MAX as i64, |
| 2495 | lim: (I16_MAX as u64) + 1, |
| 2496 | }, |
| 2497 | case Type::I32 => return IntegerRange::Signed { |
| 2498 | bits: 32, |
| 2499 | min: I32_MIN as i64, |
| 2500 | max: I32_MAX as i64, |
| 2501 | lim: (I32_MAX as u64) + 1, |
| 2502 | }, |
| 2503 | case Type::I64, Type::Int => return IntegerRange::Signed { |
| 2504 | bits: 64, |
| 2505 | min: I64_MIN, |
| 2506 | max: I64_MAX, |
| 2507 | lim: (I64_MAX as u64) + 1, |
| 2508 | }, |
| 2509 | case Type::U8 => return IntegerRange::Unsigned { bits: 8, max: U8_MAX as u64 }, |
| 2510 | case Type::U16 => return IntegerRange::Unsigned { bits: 16, max: U16_MAX as u64 }, |
| 2511 | case Type::U32 => return IntegerRange::Unsigned { bits: 32, max: parser::U32_MAX as u64 }, |
| 2512 | case Type::U64 => return IntegerRange::Unsigned { bits: 64, max: parser::U64_MAX }, |
| 2513 | else => return nil, |
| 2514 | } |
| 2515 | } |
| 2516 | |
| 2517 | /// Validate that an integer constant fits within the target type's range. |
| 2518 | fn validateConstIntRange(value: ConstValue, target: Type) -> bool { |
| 2519 | let range = integerRange(target) |
| 2520 | else panic "validateConstIntRange: expected integer type"; |
| 2521 | let case ConstValue::Int(int) = value |
| 2522 | else panic "validateConstIntRange: expected integer constant"; |
| 2523 | |
| 2524 | match range { |
| 2525 | case IntegerRange::Signed { lim, .. } => { |
| 2526 | if int.negative { |
| 2527 | if int.magnitude > lim { |
| 2528 | return false; |
| 2529 | } |
| 2530 | return true; |
| 2531 | } |
| 2532 | if int.magnitude > lim - 1 { |
| 2533 | return false; |
| 2534 | } |
| 2535 | return true; |
| 2536 | } |
| 2537 | case IntegerRange::Unsigned { max, .. } => { |
| 2538 | if int.negative or int.magnitude > max { |
| 2539 | return false; |
| 2540 | } |
| 2541 | return true; |
| 2542 | } |
| 2543 | } |
| 2544 | } |
| 2545 | |
| 2546 | /// Ensure all nested nominal types in a type are resolved. |
| 2547 | unsafe fn ensureTypeResolved 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) throws (ResolveError) { |
| 2548 | match ty { |
| 2549 | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
| 2550 | // Pointer, slice, and cell layouts do not depend on their element layout. |
| 2551 | case Type::Pointer { .. }, Type::Slice { .. }, Type::Cell { .. } => { |
| 2552 | }, |
| 2553 | case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site), |
| 2554 | case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site), |
| 2555 | else => {}, |
| 2556 | } |
| 2557 | } |
| 2558 | |
| 2559 | /// Ensure a nominal type has its body resolved. |
| 2560 | unsafe fn ensureNominalResolved 'arena (self: &mut Resolver 'arena, tyInfo: *unsafe NominalType, site: *ast::Node) |
| 2561 | throws (ResolveError) |
| 2562 | { |
| 2563 | if let case NominalType::Application(applied) = *tyInfo { |
| 2564 | try resolveNominalApplication(self, applied, site); |
| 2565 | return; |
| 2566 | } |
| 2567 | if let case NominalType::Resolving(_) = *tyInfo { |
| 2568 | throw emitError(self, site, ErrorKind::RecursiveType); |
| 2569 | } |
| 2570 | if let case NominalType::Placeholder(declNode) = *tyInfo { |
| 2571 | // When resolving on-demand (e.g. from a child module), switch to the |
| 2572 | // declaring module's scope so field type lookups find the right symbols. |
| 2573 | let prevScope = self.scope; |
| 2574 | let prevMod = self.currentMod; |
| 2575 | |
| 2576 | if let sym = symbolFor(self, declNode) { |
| 2577 | if let mid = sym.moduleId { |
| 2578 | if (mid as u32) < self.moduleScopes.len { |
| 2579 | if let ms = self.moduleScopes[mid as u32] { |
| 2580 | set self.scope = ms; |
| 2581 | set self.currentMod = mid; |
| 2582 | } |
| 2583 | } |
| 2584 | } |
| 2585 | } |
| 2586 | |
| 2587 | match declNode.value { |
| 2588 | case ast::NodeValue::RecordDecl(decl) => { |
| 2589 | try resolveRecordBody(self, declNode, decl) catch error { |
| 2590 | set self.scope = prevScope; |
| 2591 | set self.currentMod = prevMod; |
| 2592 | throw error; |
| 2593 | }; |
| 2594 | } |
| 2595 | case ast::NodeValue::UnionDecl(decl) => { |
| 2596 | try resolveUnionBody(self, declNode, decl) catch error { |
| 2597 | set self.scope = prevScope; |
| 2598 | set self.currentMod = prevMod; |
| 2599 | throw error; |
| 2600 | }; |
| 2601 | } |
| 2602 | else => {}, |
| 2603 | } |
| 2604 | set self.scope = prevScope; |
| 2605 | set self.currentMod = prevMod; |
| 2606 | } |
| 2607 | } |
| 2608 | |
| 2609 | /// Check if all elements in a node list are assignable to the target type. |
| 2610 | unsafe fn isListAssignable 'arena (self: &mut Resolver 'arena, targetType: Type, items: *[*ast::Node]) -> bool { |
| 2611 | for itemNode in items { |
| 2612 | let elemTy = typeFor(self, itemNode) |
| 2613 | else return false; |
| 2614 | if let _ = isAssignable(self, targetType, elemTy, itemNode) { |
| 2615 | // Do nothing. |
| 2616 | } else { |
| 2617 | return false; |
| 2618 | } |
| 2619 | } |
| 2620 | return true; |
| 2621 | } |
| 2622 | |
| 2623 | /// Return whether pointer classes are compatible in the current safety context. |
| 2624 | fn pointerClassesAssignable( |
| 2625 | to: types::PointerClass, |
| 2626 | from: types::PointerClass, |
| 2627 | inUnsafeContext: bool, |
| 2628 | ) -> bool { |
| 2629 | return to == from or ( |
| 2630 | to == types::PointerClass::Ref |
| 2631 | and (types::isReference(from) or from == types::PointerClass::Owned |
| 2632 | or (from == types::PointerClass::Unsafe and inUnsafeContext)) |
| 2633 | ); |
| 2634 | } |
| 2635 | |
| 2636 | /// Limit an exclusive value's implicit borrow to its owner's borrow. |
| 2637 | unsafe fn assignableValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: Type) -> Type { |
| 2638 | match source { |
| 2639 | case Type::Pointer { class, target, mutable: true } => { |
| 2640 | let usable = pointerAddressClass(self, node, class, true); |
| 2641 | return Type::Pointer { class: usable, target, mutable: true }; |
| 2642 | } |
| 2643 | case Type::Slice { class, item, mutable: true } => { |
| 2644 | let usable = pointerAddressClass(self, node, class, true); |
| 2645 | return Type::Slice { class: usable, item, mutable: true }; |
| 2646 | } |
| 2647 | case Type::TraitObject { class, traitInfo, mutable: true } => { |
| 2648 | let usable = pointerAddressClass(self, node, class, true); |
| 2649 | return Type::TraitObject { class: usable, traitInfo, mutable: true }; |
| 2650 | } |
| 2651 | case Type::Optional(inner) => { |
| 2652 | let value = assignableValueType(self, node, *inner); |
| 2653 | if typesEqual(value, *inner) { |
| 2654 | return source; |
| 2655 | } |
| 2656 | return Type::Optional(allocType(self, value)); |
| 2657 | } |
| 2658 | else => return source, |
| 2659 | } |
| 2660 | } |
| 2661 | |
| 2662 | /// Check if the `from` type is assignable to the `to` type, and return a |
| 2663 | /// coercion plan if so. |
| 2664 | /// Referenced storage requires equal element types. Function values may gain |
| 2665 | /// an unsafe call requirement. |
| 2666 | unsafe fn isAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, rval: *ast::Node) -> ?Coercion { |
| 2667 | let from = assignableValueType(self, rval, source); |
| 2668 | if to == Type::Unknown or from == Type::Unknown { |
| 2669 | return nil; |
| 2670 | } |
| 2671 | if from == Type::Undefined { |
| 2672 | if containsRegion(to) { |
| 2673 | return nil; |
| 2674 | } |
| 2675 | if to == Type::Never { |
| 2676 | return nil; |
| 2677 | } |
| 2678 | // TODO: Don't let `undefined` be used in place of functions and other |
| 2679 | // non-data types. |
| 2680 | return Coercion::Identity; |
| 2681 | } |
| 2682 | // The "never" type can always be assigned, since the code path is never |
| 2683 | // executed. |
| 2684 | if from == Type::Never { |
| 2685 | return Coercion::Identity; |
| 2686 | } |
| 2687 | if to == from { |
| 2688 | return Coercion::Identity; |
| 2689 | } |
| 2690 | if let case Type::Cell { class, permission, payload } = to { |
| 2691 | let case Type::Cell { |
| 2692 | class: sourceClass, permission: sourcePermission, payload: sourcePayload, |
| 2693 | } = from else return nil; |
| 2694 | let mut storageAssignable = pointerClassesAssignable( |
| 2695 | class, sourceClass, self.inUnsafeContext |
| 2696 | ); |
| 2697 | if let case types::PointerClass::Region(targetRegion) = class { |
| 2698 | if let case types::PointerClass::Region(sourceRegion) = sourceClass { |
| 2699 | set storageAssignable = types::regionContains(sourceRegion, targetRegion); |
| 2700 | } |
| 2701 | } |
| 2702 | if permission == sourcePermission and storageAssignable |
| 2703 | and typesEqual(*payload, *sourcePayload) |
| 2704 | { |
| 2705 | return Coercion::Identity; |
| 2706 | } |
| 2707 | return nil; |
| 2708 | } |
| 2709 | if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to { |
| 2710 | let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from |
| 2711 | else return nil; |
| 2712 | if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) { |
| 2713 | return nil; |
| 2714 | } |
| 2715 | // Allow coercion from `*T` to `*opaque`, and mutable counterparts. |
| 2716 | if *lhsTarget == Type::Opaque { |
| 2717 | if lhsMutable and not rhsMutable { |
| 2718 | return nil; |
| 2719 | } |
| 2720 | return Coercion::Identity; |
| 2721 | } |
| 2722 | if lhsMutable and not rhsMutable { |
| 2723 | return nil; |
| 2724 | } |
| 2725 | if typesEqual(*lhsTarget, *rhsTarget) { |
| 2726 | return Coercion::Identity; |
| 2727 | } |
| 2728 | return nil; |
| 2729 | } |
| 2730 | if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to { |
| 2731 | if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from { |
| 2732 | if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) |
| 2733 | or (lhsMutable and not rhsMutable) |
| 2734 | { |
| 2735 | return nil; |
| 2736 | } |
| 2737 | if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) { |
| 2738 | return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst }; |
| 2739 | } |
| 2740 | } |
| 2741 | if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from { |
| 2742 | if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) |
| 2743 | or lhsTraitInfo <> rhsTraitInfo |
| 2744 | { |
| 2745 | return nil; |
| 2746 | } |
| 2747 | if lhsMutable and not rhsMutable { |
| 2748 | return nil; |
| 2749 | } |
| 2750 | return Coercion::Identity; |
| 2751 | } |
| 2752 | return nil; |
| 2753 | } |
| 2754 | if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to { |
| 2755 | let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from |
| 2756 | else return nil; |
| 2757 | if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) |
| 2758 | or (lhsMutable and not rhsMutable) |
| 2759 | { |
| 2760 | return nil; |
| 2761 | } |
| 2762 | // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts. |
| 2763 | if *lhsItem == Type::Opaque { |
| 2764 | return Coercion::Identity; |
| 2765 | } |
| 2766 | if typesEqual(*lhsItem, *rhsItem) { |
| 2767 | return Coercion::Identity; |
| 2768 | } |
| 2769 | return nil; |
| 2770 | } |
| 2771 | match to { |
| 2772 | case Type::Array(lhs) => { |
| 2773 | let case Type::Array(rhs) = from |
| 2774 | else return nil; |
| 2775 | |
| 2776 | if lhs.length <> rhs.length { |
| 2777 | return nil; |
| 2778 | } |
| 2779 | // For array literals, check each element individually for |
| 2780 | // assignability. |
| 2781 | match rval.value { |
| 2782 | case ast::NodeValue::ArrayLit(items) => { |
| 2783 | if rhs.length == 0 and lhs.length == 0 { |
| 2784 | return Coercion::Identity; |
| 2785 | } |
| 2786 | // TODO: This won't work, because we should be setting coercions |
| 2787 | // for every list item, but we don't. It's best to not have an |
| 2788 | // `isAssignable` function and just have one that records coercions. |
| 2789 | if isListAssignable(self, *lhs.item, items) { |
| 2790 | return Coercion::Identity; |
| 2791 | } |
| 2792 | return nil; |
| 2793 | } |
| 2794 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
| 2795 | return isAssignable(self, *lhs.item, *rhs.item, repeat.item); |
| 2796 | } |
| 2797 | else => { |
| 2798 | if typesEqual(*lhs.item, *rhs.item) { |
| 2799 | return Coercion::Identity; |
| 2800 | } |
| 2801 | return nil; |
| 2802 | } |
| 2803 | } |
| 2804 | } |
| 2805 | |
| 2806 | case Type::Optional(inner) => { |
| 2807 | if from == Type::Nil { |
| 2808 | return Coercion::OptionalLift(to); |
| 2809 | } |
| 2810 | if let _ = isAssignable(self, *inner, from, rval) { |
| 2811 | return Coercion::OptionalLift(to); |
| 2812 | } |
| 2813 | if let case Type::Optional(fromInner) = from { |
| 2814 | return isAssignable(self, *inner, *fromInner, rval); |
| 2815 | } |
| 2816 | return nil; |
| 2817 | } |
| 2818 | |
| 2819 | case Type::Fn(toInfo) => { |
| 2820 | // Allow function type structural matching. |
| 2821 | if let case Type::Fn(fromInfo) = from { |
| 2822 | if fnTypeEqual(toInfo, fromInfo) or ( |
| 2823 | toInfo.isUnsafe and not fromInfo.isUnsafe |
| 2824 | and fnSignatureEqual(toInfo, fromInfo) |
| 2825 | ) { |
| 2826 | return Coercion::Identity; |
| 2827 | } |
| 2828 | } |
| 2829 | return nil; |
| 2830 | } |
| 2831 | else => { |
| 2832 | if isNumericType(to) and isNumericType(from) { |
| 2833 | // Perform range validation at compile time if possible. |
| 2834 | // For unsuffixed integer expressions (`Type::Int`), only |
| 2835 | // validate literals directly written by the programmer. |
| 2836 | // Folded results (e.g. `0 - 65`) may not fit the target |
| 2837 | // type but are valid wrapping arithmetic at runtime. |
| 2838 | if let value = constValueEntry(self, rval) { |
| 2839 | if from <> Type::Int or isIntegerLiteralExpr(rval) { |
| 2840 | if validateConstIntRange(value, to) { |
| 2841 | return Coercion::Identity; |
| 2842 | } |
| 2843 | return nil; |
| 2844 | } |
| 2845 | // Folded constant expression (e.g. `1 + 2`): if the |
| 2846 | // result fits the target, use identity. Otherwise allow |
| 2847 | // wrapping via numeric cast. |
| 2848 | if validateConstIntRange(value, to) { |
| 2849 | return Coercion::Identity; |
| 2850 | } |
| 2851 | } |
| 2852 | // Allow unsuffixed integer expressions to be inferred from context. |
| 2853 | if from == Type::Int { |
| 2854 | return Coercion::NumericCast { from, to }; |
| 2855 | } |
| 2856 | // Non-constant numeric values require an explicit cast. |
| 2857 | return nil; |
| 2858 | } |
| 2859 | } |
| 2860 | } |
| 2861 | return nil; |
| 2862 | } |
| 2863 | |
| 2864 | /// Check if two function type descriptors are structurally equivalent. |
| 2865 | fn fnTypeEqual(a: &FnType, b: &FnType) -> bool { |
| 2866 | if a.isUnsafe <> b.isUnsafe { |
| 2867 | return false; |
| 2868 | } |
| 2869 | return fnSignatureEqual(a, b); |
| 2870 | } |
| 2871 | |
| 2872 | /// Compare parameter, return, and error types of functions. |
| 2873 | fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool { |
| 2874 | if a.regions <> b.regions { |
| 2875 | return false; |
| 2876 | } |
| 2877 | if a.paramTypes.len <> b.paramTypes.len { |
| 2878 | return false; |
| 2879 | } |
| 2880 | if a.throwList.len <> b.throwList.len { |
| 2881 | return false; |
| 2882 | } |
| 2883 | if not typesEqual(*a.returnType, *b.returnType) { |
| 2884 | return false; |
| 2885 | } |
| 2886 | for i in 0..a.paramTypes.len { |
| 2887 | if not typesEqual(*a.paramTypes[i], *b.paramTypes[i]) { |
| 2888 | return false; |
| 2889 | } |
| 2890 | } |
| 2891 | for i in 0..a.throwList.len { |
| 2892 | if not typesEqual(*a.throwList[i], *b.throwList[i]) { |
| 2893 | return false; |
| 2894 | } |
| 2895 | } |
| 2896 | return true; |
| 2897 | } |
| 2898 | |
| 2899 | /// Check if two types are structurally equal. |
| 2900 | export fn typesEqual(a: Type, b: Type) -> bool { |
| 2901 | // Nominal and trait types compare by descriptor identity. |
| 2902 | if a == b { |
| 2903 | return true; |
| 2904 | } |
| 2905 | if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a { |
| 2906 | let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b |
| 2907 | else return false; |
| 2908 | return aClass == bClass and aMutable == bMutable |
| 2909 | and typesEqual(*aTarget, *bTarget); |
| 2910 | } |
| 2911 | if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a { |
| 2912 | let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b |
| 2913 | else return false; |
| 2914 | return aClass == bClass and aMutable == bMutable |
| 2915 | and typesEqual(*aItem, *bItem); |
| 2916 | } |
| 2917 | match a { |
| 2918 | case Type::Cell { class, permission, payload } => { |
| 2919 | let case Type::Cell { |
| 2920 | class: otherClass, permission: otherPermission, payload: other, |
| 2921 | } = b else return false; |
| 2922 | return class == otherClass and permission == otherPermission |
| 2923 | and typesEqual(*payload, *other); |
| 2924 | } |
| 2925 | case Type::Array(aa) => { |
| 2926 | let case Type::Array(ab) = b else return false; |
| 2927 | return aa.length == ab.length and typesEqual(*aa.item, *ab.item); |
| 2928 | } |
| 2929 | case Type::Optional(oa) => { |
| 2930 | let case Type::Optional(ob) = b else return false; |
| 2931 | return typesEqual(*oa, *ob); |
| 2932 | } |
| 2933 | case Type::Fn(fa) => { |
| 2934 | let case Type::Fn(fb) = b else return false; |
| 2935 | return fnTypeEqual(fa, fb); |
| 2936 | } |
| 2937 | else => return false, |
| 2938 | } |
| 2939 | } |
| 2940 | |
| 2941 | /// Compare types after lexical region arguments are erased. |
| 2942 | /// Nominal types retain their source declaration identity. |
| 2943 | export unsafe fn erasedTypesEqual(a: Type, b: Type) -> bool { |
| 2944 | if typesEqual(a, b) { |
| 2945 | return true; |
| 2946 | } |
| 2947 | match a { |
| 2948 | case Type::Cell { class, permission, payload } => { |
| 2949 | let case Type::Cell { |
| 2950 | class: otherClass, permission: otherPermission, payload: other, |
| 2951 | } = b else return false; |
| 2952 | if (permission == nil) <> (otherPermission == nil) { |
| 2953 | return false; |
| 2954 | } |
| 2955 | return erasedClassesEqual(class, otherClass) and erasedTypesEqual(*payload, *other); |
| 2956 | } |
| 2957 | case Type::Session(_) => { |
| 2958 | let case Type::Session(_) = b else return false; |
| 2959 | return true; |
| 2960 | } |
| 2961 | case Type::Nominal(left) => { |
| 2962 | let case Type::Nominal(right) = b else return false; |
| 2963 | let mut leftBase = left; |
| 2964 | let mut rightBase = right; |
| 2965 | if let app = nominalApplication(left) { |
| 2966 | set leftBase = app.base; |
| 2967 | } |
| 2968 | if let app = nominalApplication(right) { |
| 2969 | set rightBase = app.base; |
| 2970 | } |
| 2971 | return leftBase == rightBase; |
| 2972 | } |
| 2973 | case Type::Pointer { class, target, mutable } => { |
| 2974 | let case Type::Pointer { class: otherClass, target: other, mutable: otherMutable } = b |
| 2975 | else return false; |
| 2976 | return erasedClassesEqual(class, otherClass) and mutable == otherMutable |
| 2977 | and erasedTypesEqual(*target, *other); |
| 2978 | } |
| 2979 | case Type::Slice { class, item, mutable } => { |
| 2980 | let case Type::Slice { class: otherClass, item: other, mutable: otherMutable } = b |
| 2981 | else return false; |
| 2982 | return erasedClassesEqual(class, otherClass) and mutable == otherMutable |
| 2983 | and erasedTypesEqual(*item, *other); |
| 2984 | } |
| 2985 | case Type::TraitObject { class, traitInfo, mutable } => { |
| 2986 | let case Type::TraitObject { class: otherClass, traitInfo: other, mutable: otherMutable } = b |
| 2987 | else return false; |
| 2988 | return erasedClassesEqual(class, otherClass) and mutable == otherMutable and traitInfo == other; |
| 2989 | } |
| 2990 | case Type::Array(left) => { |
| 2991 | let case Type::Array(right) = b else return false; |
| 2992 | return left.length == right.length and erasedTypesEqual(*left.item, *right.item); |
| 2993 | } |
| 2994 | case Type::Optional(left) => { |
| 2995 | let case Type::Optional(right) = b else return false; |
| 2996 | return erasedTypesEqual(*left, *right); |
| 2997 | } |
| 2998 | case Type::Fn(left) => { |
| 2999 | let case Type::Fn(right) = b else return false; |
| 3000 | if left.isUnsafe <> right.isUnsafe or left.paramTypes.len <> right.paramTypes.len |
| 3001 | or left.throwList.len <> right.throwList.len { |
| 3002 | return false; |
| 3003 | } |
| 3004 | for ty, i in left.paramTypes { |
| 3005 | if not erasedTypesEqual(*ty, *right.paramTypes[i]) { |
| 3006 | return false; |
| 3007 | } |
| 3008 | } |
| 3009 | for ty, i in left.throwList { |
| 3010 | if not erasedTypesEqual(*ty, *right.throwList[i]) { |
| 3011 | return false; |
| 3012 | } |
| 3013 | } |
| 3014 | return erasedTypesEqual(*left.returnType, *right.returnType); |
| 3015 | } |
| 3016 | else => return false, |
| 3017 | } |
| 3018 | } |
| 3019 | |
| 3020 | /// Compare pointer classes without lexical region identities. |
| 3021 | fn erasedClassesEqual(a: types::PointerClass, b: types::PointerClass) -> bool { |
| 3022 | return a == b or (types::isReference(a) and types::isReference(b)); |
| 3023 | } |
| 3024 | |
| 3025 | /// Require distinct runtime tags for errors with different source types. |
| 3026 | unsafe fn validateErrorTag 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type, errors: *[*Type]) throws (ResolveError) { |
| 3027 | for other in errors { |
| 3028 | if not typesEqual(ty, *other) and erasedTypesEqual(ty, *other) { |
| 3029 | throw emitError(self, node, ErrorKind::AmbiguousRegionalError); |
| 3030 | } |
| 3031 | } |
| 3032 | } |
| 3033 | |
| 3034 | /// Return whether `ty` is a direct reference. |
| 3035 | export fn isRefType(ty: Type) -> bool { |
| 3036 | match ty { |
| 3037 | case Type::Cell { class, .. } => return types::isReference(class), |
| 3038 | case Type::Pointer { class, .. } => return types::isReference(class), |
| 3039 | case Type::Slice { class, .. } => return types::isReference(class), |
| 3040 | case Type::TraitObject { class, .. } => return types::isReference(class), |
| 3041 | else => return false, |
| 3042 | } |
| 3043 | } |
| 3044 | |
| 3045 | /// Get the region of a direct named reference. |
| 3046 | fn referenceRegion(ty: Type) -> ?*unsafe types::Region { |
| 3047 | let mut class = types::PointerClass::Ref; |
| 3048 | match ty { |
| 3049 | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
| 3050 | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
| 3051 | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
| 3052 | case Type::TraitObject { class: objectClass, .. } => set class = objectClass, |
| 3053 | else => return nil, |
| 3054 | } |
| 3055 | if let case types::PointerClass::Region(region) = class { |
| 3056 | return region; |
| 3057 | } |
| 3058 | return nil; |
| 3059 | } |
| 3060 | |
| 3061 | /// Require every free region in a value type to remain in lexical scope. |
| 3062 | unsafe fn validateRegionDependencies 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
| 3063 | throws (ResolveError) |
| 3064 | { |
| 3065 | try validateRegionStorage(self, node, ty, nil); |
| 3066 | } |
| 3067 | |
| 3068 | /// Check a dependency against storage lifetime or current lexical visibility. |
| 3069 | unsafe fn regionCoversStorage( |
| 3070 | scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region |
| 3071 | ) -> bool { |
| 3072 | if let region = destination { |
| 3073 | return types::regionContains(dependency, region); |
| 3074 | } |
| 3075 | return regionInScope(scope, dependency.id); |
| 3076 | } |
| 3077 | |
| 3078 | /// Require stored references to cover the lifetime of checked destination storage. |
| 3079 | unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type) |
| 3080 | throws (ResolveError) |
| 3081 | { |
| 3082 | if let case types::PointerClass::Region(region) = addressStorageClass(self, place) { |
| 3083 | try validateRegionStorage(self, value, ty, region); |
| 3084 | } |
| 3085 | } |
| 3086 | |
| 3087 | /// Require all type dependencies to cover the destination or active lexical scope. |
| 3088 | /// Permission identities are visibility dependencies rather than storage lifetimes. |
| 3089 | unsafe fn validateRegionStorage 'arena ( |
| 3090 | self: &mut Resolver 'arena, node: *ast::Node, ty: Type, destination: ?*unsafe types::Region |
| 3091 | ) |
| 3092 | throws (ResolveError) |
| 3093 | { |
| 3094 | let generation = nextNominalTraversalGeneration(self); |
| 3095 | try validateRegionStorageType(self, node, ty, destination, generation); |
| 3096 | } |
| 3097 | |
| 3098 | /// Walk one value type once per applied nominal descriptor. |
| 3099 | unsafe fn validateRegionStorageType 'arena ( |
| 3100 | self: &mut Resolver 'arena, |
| 3101 | node: *ast::Node, |
| 3102 | ty: Type, |
| 3103 | destination: ?*unsafe types::Region, |
| 3104 | generation: u32, |
| 3105 | ) |
| 3106 | throws (ResolveError) |
| 3107 | { |
| 3108 | if let case Type::Session(region) = ty { |
| 3109 | if not regionCoversStorage(self.regionScope, region, destination) { |
| 3110 | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
| 3111 | } |
| 3112 | } |
| 3113 | if let region = referenceRegion(ty) { |
| 3114 | if not regionCoversStorage(self.regionScope, region, destination) { |
| 3115 | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
| 3116 | } |
| 3117 | } |
| 3118 | match ty { |
| 3119 | case Type::Pointer { target, .. } => |
| 3120 | try validateRegionStorageType( |
| 3121 | self, node, *target, destination, generation, |
| 3122 | ), |
| 3123 | case Type::Slice { item, .. } => |
| 3124 | try validateRegionStorageType( |
| 3125 | self, node, *item, destination, generation, |
| 3126 | ), |
| 3127 | case Type::Cell { permission, payload, .. } => { |
| 3128 | if let region = permission; not regionInScope(self.regionScope, region.id) { |
| 3129 | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
| 3130 | } |
| 3131 | try validateRegionStorageType( |
| 3132 | self, node, *payload, destination, generation, |
| 3133 | ); |
| 3134 | } |
| 3135 | case Type::Array(array) => |
| 3136 | try validateRegionStorageType( |
| 3137 | self, node, *array.item, destination, generation, |
| 3138 | ), |
| 3139 | case Type::Optional(inner) => |
| 3140 | try validateRegionStorageType( |
| 3141 | self, node, *inner, destination, generation, |
| 3142 | ), |
| 3143 | case Type::Nominal(info) => { |
| 3144 | let applied = nominalApplication(info) else return; |
| 3145 | for region in applied.arguments { |
| 3146 | if not regionInScope(self.regionScope, region.id) { |
| 3147 | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
| 3148 | } |
| 3149 | } |
| 3150 | if not visitNominalApplication( |
| 3151 | applied, generation, RegionTypeRole::StorageValidation, |
| 3152 | ) { |
| 3153 | return; |
| 3154 | } |
| 3155 | match *info { |
| 3156 | case NominalType::Record(recordType) => { |
| 3157 | for field in recordType.fields { |
| 3158 | try validateRegionStorageType( |
| 3159 | self, node, field.fieldType, destination, generation, |
| 3160 | ); |
| 3161 | } |
| 3162 | } |
| 3163 | case NominalType::Union(unionType) => { |
| 3164 | for variant in unionType.variants { |
| 3165 | try validateRegionStorageType( |
| 3166 | self, node, variant.valueType, destination, generation, |
| 3167 | ); |
| 3168 | } |
| 3169 | } |
| 3170 | // Unresolved applications have no member view to inspect yet. |
| 3171 | case NominalType::Placeholder(_), NominalType::Resolving(_), |
| 3172 | NominalType::Application(_) => return, |
| 3173 | } |
| 3174 | } |
| 3175 | case Type::Fn(info) => { |
| 3176 | if info.regions <> nil { |
| 3177 | return; |
| 3178 | } |
| 3179 | for parameter in info.paramTypes { |
| 3180 | try validateRegionStorageType( |
| 3181 | self, node, *parameter, destination, generation, |
| 3182 | ); |
| 3183 | } |
| 3184 | for error in info.throwList { |
| 3185 | try validateRegionStorageType( |
| 3186 | self, node, *error, destination, generation, |
| 3187 | ); |
| 3188 | } |
| 3189 | try validateRegionStorageType( |
| 3190 | self, node, *info.returnType, destination, generation, |
| 3191 | ); |
| 3192 | } |
| 3193 | else => {} |
| 3194 | } |
| 3195 | } |
| 3196 | |
| 3197 | /// Return whether a value type carries a named storage lifetime. |
| 3198 | /// Cell permission identities authorize access and do not constrain storage. |
| 3199 | unsafe fn containsStorageRegion 'arena ( |
| 3200 | self: &mut Resolver 'arena, node: *ast::Node, root: Type |
| 3201 | ) -> bool throws (ResolveError) { |
| 3202 | let generation = nextNominalTraversalGeneration(self); |
| 3203 | return try typeContainsStorageRegion(self, node, root, generation); |
| 3204 | } |
| 3205 | |
| 3206 | /// Search one value type once per applied nominal descriptor. |
| 3207 | unsafe fn typeContainsStorageRegion 'arena ( |
| 3208 | self: &mut Resolver 'arena, node: *ast::Node, ty: Type, generation: u32 |
| 3209 | ) -> bool throws (ResolveError) { |
| 3210 | match ty { |
| 3211 | case Type::Cell { class, payload, .. } => { |
| 3212 | if let case types::PointerClass::Region(_) = class { |
| 3213 | return true; |
| 3214 | } |
| 3215 | return try typeContainsStorageRegion(self, node, *payload, generation); |
| 3216 | } |
| 3217 | case Type::Session(_) => return true, |
| 3218 | case Type::Pointer { class, target, .. } => { |
| 3219 | if let case types::PointerClass::Region(_) = class { |
| 3220 | return true; |
| 3221 | } |
| 3222 | return try typeContainsStorageRegion(self, node, *target, generation); |
| 3223 | } |
| 3224 | case Type::Slice { class, item, .. } => { |
| 3225 | if let case types::PointerClass::Region(_) = class { |
| 3226 | return true; |
| 3227 | } |
| 3228 | return try typeContainsStorageRegion(self, node, *item, generation); |
| 3229 | } |
| 3230 | case Type::TraitObject { class, .. } => { |
| 3231 | if let case types::PointerClass::Region(_) = class { |
| 3232 | return true; |
| 3233 | } |
| 3234 | return false; |
| 3235 | } |
| 3236 | case Type::Array(array) => |
| 3237 | return try typeContainsStorageRegion(self, node, *array.item, generation), |
| 3238 | case Type::Optional(inner) => |
| 3239 | return try typeContainsStorageRegion(self, node, *inner, generation), |
| 3240 | case Type::Range { start, end } => { |
| 3241 | if let startType = start; |
| 3242 | try typeContainsStorageRegion(self, node, *startType, generation) |
| 3243 | { |
| 3244 | return true; |
| 3245 | } |
| 3246 | if let endType = end { |
| 3247 | return try typeContainsStorageRegion(self, node, *endType, generation); |
| 3248 | } |
| 3249 | return false; |
| 3250 | } |
| 3251 | case Type::Nominal(info) => { |
| 3252 | try ensureNominalResolved(self, info, node); |
| 3253 | let applied = nominalApplication(info) else return false; |
| 3254 | if not visitNominalApplication( |
| 3255 | applied, generation, RegionTypeRole::StoragePresence, |
| 3256 | ) { |
| 3257 | return false; |
| 3258 | } |
| 3259 | match *info { |
| 3260 | case NominalType::Record(recordType) => { |
| 3261 | for field in recordType.fields { |
| 3262 | if try typeContainsStorageRegion( |
| 3263 | self, node, field.fieldType, generation, |
| 3264 | ) { |
| 3265 | return true; |
| 3266 | } |
| 3267 | } |
| 3268 | } |
| 3269 | case NominalType::Union(unionType) => { |
| 3270 | for variant in unionType.variants { |
| 3271 | if try typeContainsStorageRegion( |
| 3272 | self, node, variant.valueType, generation, |
| 3273 | ) { |
| 3274 | return true; |
| 3275 | } |
| 3276 | } |
| 3277 | } |
| 3278 | case NominalType::Placeholder(_), NominalType::Resolving(_), |
| 3279 | NominalType::Application(_) => |
| 3280 | panic "typeContainsStorageRegion: unresolved nominal type", |
| 3281 | } |
| 3282 | return false; |
| 3283 | } |
| 3284 | case Type::Fn(info) => { |
| 3285 | if info.regions <> nil { |
| 3286 | return false; |
| 3287 | } |
| 3288 | for parameter in info.paramTypes { |
| 3289 | if try typeContainsStorageRegion( |
| 3290 | self, node, *parameter, generation, |
| 3291 | ) { |
| 3292 | return true; |
| 3293 | } |
| 3294 | } |
| 3295 | for error in info.throwList { |
| 3296 | if try typeContainsStorageRegion(self, node, *error, generation) { |
| 3297 | return true; |
| 3298 | } |
| 3299 | } |
| 3300 | return try typeContainsStorageRegion( |
| 3301 | self, node, *info.returnType, generation, |
| 3302 | ); |
| 3303 | } |
| 3304 | else => return false, |
| 3305 | } |
| 3306 | } |
| 3307 | |
| 3308 | /// Return whether a type has an explicit region dependency. |
| 3309 | unsafe fn containsRegion(ty: Type) -> bool { |
| 3310 | match ty { |
| 3311 | case Type::Cell { class, permission, payload } => { |
| 3312 | if permission <> nil { |
| 3313 | return true; |
| 3314 | } |
| 3315 | if let case types::PointerClass::Region(_) = class { |
| 3316 | return true; |
| 3317 | } |
| 3318 | return containsRegion(*payload); |
| 3319 | } |
| 3320 | case Type::Session(_) => return true, |
| 3321 | case Type::Pointer { class, target, .. } => { |
| 3322 | if let case types::PointerClass::Region(_) = class { |
| 3323 | return true; |
| 3324 | } |
| 3325 | return containsRegion(*target); |
| 3326 | } |
| 3327 | case Type::Slice { class, item, .. } => { |
| 3328 | if let case types::PointerClass::Region(_) = class { |
| 3329 | return true; |
| 3330 | } |
| 3331 | return containsRegion(*item); |
| 3332 | } |
| 3333 | case Type::TraitObject { class, .. } => { |
| 3334 | if let case types::PointerClass::Region(_) = class { |
| 3335 | return true; |
| 3336 | } |
| 3337 | return false; |
| 3338 | } |
| 3339 | case Type::Array(array) => return containsRegion(*array.item), |
| 3340 | case Type::Optional(inner) => return containsRegion(*inner), |
| 3341 | case Type::Fn(info) => { |
| 3342 | if info.regions <> nil or containsRegion(*info.returnType) { |
| 3343 | return true; |
| 3344 | } |
| 3345 | for param in info.paramTypes { |
| 3346 | if containsRegion(*param) { |
| 3347 | return true; |
| 3348 | } |
| 3349 | } |
| 3350 | for error in info.throwList { |
| 3351 | if containsRegion(*error) { |
| 3352 | return true; |
| 3353 | } |
| 3354 | } |
| 3355 | return false; |
| 3356 | } |
| 3357 | case Type::Nominal(info) => return nominalApplication(info) <> nil, |
| 3358 | else => return false, |
| 3359 | } |
| 3360 | } |
| 3361 | |
| 3362 | /// Return whether a stored type contains a reference without a named region. |
| 3363 | fn containsUnscopedRef(ty: Type) -> bool { |
| 3364 | if isRefType(ty) and referenceRegion(ty) == nil { |
| 3365 | return true; |
| 3366 | } |
| 3367 | if let case Type::Pointer { target, .. } = ty { |
| 3368 | return containsUnscopedRef(*target); |
| 3369 | } |
| 3370 | if let case Type::Slice { item, .. } = ty { |
| 3371 | return containsUnscopedRef(*item); |
| 3372 | } |
| 3373 | match ty { |
| 3374 | case Type::Cell { payload, .. } => return containsUnscopedRef(*payload), |
| 3375 | case Type::Array(array) => return containsUnscopedRef(*array.item), |
| 3376 | case Type::Optional(inner) => return containsUnscopedRef(*inner), |
| 3377 | case Type::Fn(info) => return info.regions <> nil, |
| 3378 | // Nominal declarations validate their own fields and variants. |
| 3379 | // Treating them as leaves also terminates recursive pointer types. |
| 3380 | case Type::Nominal(_) => return false, |
| 3381 | else => return false, |
| 3382 | } |
| 3383 | } |
| 3384 | |
| 3385 | /// Return whether `ty` may be duplicated implicitly. |
| 3386 | export unsafe fn isCopy(ty: Type) -> bool { |
| 3387 | match ty { |
| 3388 | case Type::Session(_) => return false, |
| 3389 | case Type::Pointer { class, mutable, .. } => |
| 3390 | return class == types::PointerClass::Unsafe or not mutable, |
| 3391 | case Type::Slice { class, mutable, .. } => |
| 3392 | return class == types::PointerClass::Unsafe or not mutable, |
| 3393 | case Type::TraitObject { class, mutable, .. } => |
| 3394 | return class == types::PointerClass::Unsafe or not mutable, |
| 3395 | case Type::Array(array) => return isCopy(*array.item), |
| 3396 | case Type::Optional(inner) => return isCopy(*inner), |
| 3397 | case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy, |
| 3398 | case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy, |
| 3399 | case Type::Nominal(NominalType::Application(applied)) => return isCopy(Type::Nominal(applied.base)), |
| 3400 | case Type::Nominal(NominalType::Placeholder(_)), Type::Nominal(NominalType::Resolving(_)) => return false, |
| 3401 | else => return true, |
| 3402 | } |
| 3403 | } |
| 3404 | |
| 3405 | /// Return whether a type must be consumed exactly once. |
| 3406 | export unsafe fn isLinear(ty: Type) -> bool { |
| 3407 | match ty { |
| 3408 | case Type::Nominal(NominalType::Application(applied)) => return isLinear(Type::Nominal(applied.base)), |
| 3409 | case Type::Array(array) => return isLinear(*array.item), |
| 3410 | case Type::Optional(inner) => return isLinear(*inner), |
| 3411 | case Type::Nominal(NominalType::Record(recInfo)) => { |
| 3412 | if recInfo.declaredLinear { |
| 3413 | return true; |
| 3414 | } |
| 3415 | for field in recInfo.fields { |
| 3416 | if isLinear(field.fieldType) { |
| 3417 | return true; |
| 3418 | } |
| 3419 | } |
| 3420 | return false; |
| 3421 | } |
| 3422 | case Type::Nominal(NominalType::Union(unionType)) => { |
| 3423 | if unionType.declaredLinear { |
| 3424 | return true; |
| 3425 | } |
| 3426 | for variant in unionType.variants { |
| 3427 | if isLinear(variant.valueType) { |
| 3428 | return true; |
| 3429 | } |
| 3430 | } |
| 3431 | return false; |
| 3432 | } |
| 3433 | else => return false, |
| 3434 | } |
| 3435 | } |
| 3436 | |
| 3437 | /// Return whether a by-value use moves `ty`. |
| 3438 | unsafe fn isMoveOnly(ty: Type) -> bool { |
| 3439 | return not isCopy(ty); |
| 3440 | } |
| 3441 | |
| 3442 | /// Return whether `ty` is a direct unsafe pointer-like value. |
| 3443 | fn isUnsafePointerType(ty: Type) -> bool { |
| 3444 | match ty { |
| 3445 | case Type::Cell { class: types::PointerClass::Unsafe, .. }, |
| 3446 | Type::Pointer { class: types::PointerClass::Unsafe, .. }, |
| 3447 | Type::Slice { class: types::PointerClass::Unsafe, .. }, |
| 3448 | Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true, |
| 3449 | else => return false, |
| 3450 | } |
| 3451 | } |
| 3452 | |
| 3453 | /// Get the record info from a record type. |
| 3454 | export unsafe fn getRecord(ty: Type) -> ?RecordType { |
| 3455 | let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil; |
| 3456 | return recInfo; |
| 3457 | } |
| 3458 | |
| 3459 | /// Auto-dereference a type: if it's a pointer, return the target type. |
| 3460 | export fn autoDeref(ty: Type) -> Type { |
| 3461 | if let case Type::Pointer { target, .. } = ty { |
| 3462 | return *target; |
| 3463 | } |
| 3464 | return ty; |
| 3465 | } |
| 3466 | |
| 3467 | /// Get field info for a record-like type (records, slices) by field index. |
| 3468 | export unsafe fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
| 3469 | if let case Type::Slice { class, item, mutable } = ty { |
| 3470 | match index { |
| 3471 | case 0 => return RecordField { |
| 3472 | name: PTR_FIELD, |
| 3473 | fieldType: Type::Pointer { class, target: item, mutable }, |
| 3474 | offset: 0, |
| 3475 | }, |
| 3476 | case 1 => return RecordField { |
| 3477 | name: LEN_FIELD, |
| 3478 | fieldType: Type::U32, |
| 3479 | offset: PTR_SIZE as i32, |
| 3480 | }, |
| 3481 | case 2 => return RecordField { |
| 3482 | name: CAP_FIELD, |
| 3483 | fieldType: Type::U32, |
| 3484 | offset: PTR_SIZE as i32 + 4, |
| 3485 | }, |
| 3486 | else => return nil, |
| 3487 | } |
| 3488 | } |
| 3489 | if let case Type::Nominal(NominalType::Record(recInfo)) = ty; |
| 3490 | index < recInfo.fields.len |
| 3491 | { |
| 3492 | return recInfo.fields[index]; |
| 3493 | } |
| 3494 | return nil; |
| 3495 | } |
| 3496 | |
| 3497 | /// Check if the two types can be compared for equality. |
| 3498 | unsafe fn isComparable(left: Type, right: Type) -> bool { |
| 3499 | if left == Type::Unknown or right == Type::Unknown { |
| 3500 | return false; |
| 3501 | } |
| 3502 | if left == right { |
| 3503 | return true; |
| 3504 | } |
| 3505 | // Comparisons with optionals. |
| 3506 | if let case Type::Optional(l) = left { |
| 3507 | if let case Type::Optional(r) = right { |
| 3508 | return isComparable(*l, *r); |
| 3509 | } else if right == Type::Nil { |
| 3510 | return true; |
| 3511 | } |
| 3512 | return isComparable(*l, right); |
| 3513 | } else if let case Type::Optional(_) = right { |
| 3514 | return isComparable(right, left); // Flip order. |
| 3515 | } |
| 3516 | // Pointer comparisons ignore mutability. |
| 3517 | if let case Type::Pointer { target: lTarget, .. } = left { |
| 3518 | if let case Type::Pointer { target: rTarget, .. } = right { |
| 3519 | return typesEqual(*lTarget, *rTarget); |
| 3520 | } |
| 3521 | } |
| 3522 | // Numeric types. |
| 3523 | if isNumericType(left) and isNumericType(right) { |
| 3524 | return true; |
| 3525 | } |
| 3526 | return false; |
| 3527 | } |
| 3528 | |
| 3529 | /// Check if the `from` type is assignable to the `to` type, and return a |
| 3530 | /// coercion plan if so, or throw an error if not. |
| 3531 | unsafe fn expectAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, site: *ast::Node) -> Coercion throws (ResolveError) { |
| 3532 | let from = assignableValueType(self, site, source); |
| 3533 | if isRefType(to) and isUnsafePointerType(from) { |
| 3534 | try requireUnsafe(self, site); |
| 3535 | } |
| 3536 | // Ensure any nested nominal types are resolved before checking assignability. |
| 3537 | try ensureTypeResolved(self, to, site); |
| 3538 | if let coercion = isAssignable(self, to, from, site) { |
| 3539 | return setNodeCoercion(self, site, coercion); |
| 3540 | } |
| 3541 | throw emitTypeMismatch(self, site, TypeMismatch { |
| 3542 | expected: to, |
| 3543 | actual: from, |
| 3544 | }); |
| 3545 | } |
| 3546 | |
| 3547 | /// Check that a type is optional, otherwise throw an error. |
| 3548 | unsafe fn checkOptional 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *Type |
| 3549 | throws (ResolveError) |
| 3550 | { |
| 3551 | if let case Type::Optional(inner) = try infer(self, node) { |
| 3552 | return inner; |
| 3553 | } |
| 3554 | throw emitError(self, node, ErrorKind::ExpectedOptional); |
| 3555 | } |
| 3556 | |
| 3557 | /// Check that a node's type is equal to the expected type. |
| 3558 | unsafe fn checkEqual 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type |
| 3559 | throws (ResolveError) |
| 3560 | { |
| 3561 | let actualTy = try visit(self, node, expected); |
| 3562 | if actualTy <> expected { |
| 3563 | throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy }); |
| 3564 | } |
| 3565 | return actualTy; |
| 3566 | } |
| 3567 | |
| 3568 | /// Bind an identifier in the given scope. |
| 3569 | unsafe fn bindIdent 'arena ( |
| 3570 | self: &mut Resolver 'arena, |
| 3571 | name: *[u8], |
| 3572 | owner: *ast::Node, |
| 3573 | data: SymbolData, |
| 3574 | attrs: u32, |
| 3575 | scope: *unsafe mut Scope |
| 3576 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3577 | let sym = allocSymbol(self, data, name, owner, attrs); |
| 3578 | try addSymbolToScope(self, sym, scope, owner); |
| 3579 | setNodeSymbol(self, owner, sym); |
| 3580 | |
| 3581 | return sym; |
| 3582 | } |
| 3583 | |
| 3584 | /// Add a symbol to the given scope. |
| 3585 | unsafe fn addSymbolToScope 'arena (self: &mut Resolver 'arena, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) { |
| 3586 | for i in 0..scope.symbolsLen { |
| 3587 | if scope.symbols[i].name == sym.name { |
| 3588 | throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name)); |
| 3589 | } |
| 3590 | } |
| 3591 | if scope.symbolsLen >= scope.symbols.len { |
| 3592 | throw emitError(self, site, ErrorKind::SymbolOverflow); |
| 3593 | } |
| 3594 | // Preserve the defining module when importing an existing symbol into |
| 3595 | // another module's scope. |
| 3596 | if sym.moduleId == nil { |
| 3597 | if let modId = scope.moduleId { |
| 3598 | set sym.moduleId = modId; |
| 3599 | } |
| 3600 | } |
| 3601 | set scope.symbols[scope.symbolsLen] = sym; |
| 3602 | set scope.symbolsLen += 1; |
| 3603 | } |
| 3604 | |
| 3605 | /// Bind a value identifier in the current scope. |
| 3606 | /// Returns `nil` if the identifier is a placeholder (`_`). |
| 3607 | unsafe fn bindValueIdent 'arena ( |
| 3608 | self: &mut Resolver 'arena, |
| 3609 | ident: *ast::Node, |
| 3610 | owner: *ast::Node, |
| 3611 | type: Type, |
| 3612 | mutable: bool, |
| 3613 | alignment: u32, |
| 3614 | attrs: u32 |
| 3615 | ) -> ?*unsafe mut Symbol throws (ResolveError) { |
| 3616 | if let case ast::NodeValue::Placeholder = ident.value { |
| 3617 | setNodeType(self, owner, type); |
| 3618 | return nil; |
| 3619 | } |
| 3620 | let name = try nodeName(self, ident); |
| 3621 | let data = SymbolData::Value { mutable, alignment, type, addressTaken: false }; |
| 3622 | let scope = self.scope; |
| 3623 | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
| 3624 | if ident <> owner { |
| 3625 | setNodeSymbol(self, ident, sym); |
| 3626 | } |
| 3627 | setNodeType(self, owner, type); |
| 3628 | setNodeType(self, ident, type); |
| 3629 | |
| 3630 | // Track number of local bindings for lowering stage. |
| 3631 | if let owner = self.currentFnNode { |
| 3632 | set self.nodeData.entries[owner.id].localCount += 1; |
| 3633 | } |
| 3634 | return sym; |
| 3635 | } |
| 3636 | |
| 3637 | /// Bind a constant identifier in the current scope. |
| 3638 | unsafe fn bindConstIdent 'arena ( |
| 3639 | self: &mut Resolver 'arena, |
| 3640 | ident: *ast::Node, |
| 3641 | owner: *ast::Node, |
| 3642 | type: Type, |
| 3643 | val: ?ConstValue, |
| 3644 | attrs: u32 |
| 3645 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3646 | let name = try nodeName(self, ident); |
| 3647 | let data = SymbolData::Constant { type, value: val }; |
| 3648 | let scope = self.scope; |
| 3649 | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
| 3650 | setNodeType(self, owner, type); |
| 3651 | setNodeType(self, ident, type); |
| 3652 | |
| 3653 | return sym; |
| 3654 | } |
| 3655 | |
| 3656 | /// Bind a module identifier in the given scope. |
| 3657 | /// This is used when declaring modules with `mod` or |
| 3658 | /// importing modules with `use`. |
| 3659 | unsafe fn bindModuleIdent 'arena ( |
| 3660 | self: &mut Resolver 'arena, |
| 3661 | entry: *module::ModuleEntry, |
| 3662 | scope: *unsafe mut Scope, |
| 3663 | owner: *ast::Node, |
| 3664 | attrs: u32, |
| 3665 | bindingScope: *unsafe mut Scope |
| 3666 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3667 | let data = SymbolData::Module { entry, scope }; |
| 3668 | let name = entry.name; |
| 3669 | |
| 3670 | return try bindIdent(self, name, owner, data, attrs, bindingScope); |
| 3671 | } |
| 3672 | |
| 3673 | /// Bind a type identifier in the current scope. |
| 3674 | unsafe fn bindTypeIdent 'arena ( |
| 3675 | self: &mut Resolver 'arena, |
| 3676 | ident: *ast::Node, |
| 3677 | owner: *ast::Node, |
| 3678 | type: *unsafe mut NominalType, |
| 3679 | attrs: u32 |
| 3680 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3681 | let name = try nodeName(self, ident); |
| 3682 | let data = SymbolData::Type(type); |
| 3683 | let scope = self.scope; |
| 3684 | return try bindIdent(self, name, owner, data, attrs, scope); |
| 3685 | } |
| 3686 | |
| 3687 | /// Predicate that matches any symbol. |
| 3688 | fn isAnySymbol(_sym: &Symbol) -> bool { |
| 3689 | return true; |
| 3690 | } |
| 3691 | |
| 3692 | /// Predicate that matches value or constant symbols. |
| 3693 | fn isValueSymbol(sym: &Symbol) -> bool { |
| 3694 | if let case SymbolData::Value { .. } = sym.data { |
| 3695 | return true; |
| 3696 | } |
| 3697 | if let case SymbolData::Constant { .. } = sym.data { |
| 3698 | return true; |
| 3699 | } |
| 3700 | return false; |
| 3701 | } |
| 3702 | |
| 3703 | /// Predicate that matches type symbols. |
| 3704 | fn isTypeSymbol(sym: &Symbol) -> bool { |
| 3705 | if let case SymbolData::Type(_) = sym.data { |
| 3706 | return true; |
| 3707 | } |
| 3708 | return false; |
| 3709 | } |
| 3710 | |
| 3711 | /// Find a symbol by name in a specific scope, filtered by a predicate. |
| 3712 | unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol { |
| 3713 | for i in 0..scope.symbolsLen { |
| 3714 | let sym = scope.symbols[i]; |
| 3715 | if sym.name == name and predicate(sym) { |
| 3716 | return sym; |
| 3717 | } |
| 3718 | } |
| 3719 | return nil; |
| 3720 | } |
| 3721 | |
| 3722 | /// Find a symbol by name, traversing scopes upwards, filtered by a predicate. |
| 3723 | unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol { |
| 3724 | let mut curr = scope; |
| 3725 | loop { |
| 3726 | if let sym = findInScope(curr, name, predicate) { |
| 3727 | return sym; |
| 3728 | } |
| 3729 | if let parent = curr.parent { |
| 3730 | set curr = parent; |
| 3731 | } else { |
| 3732 | break; |
| 3733 | } |
| 3734 | } |
| 3735 | return nil; |
| 3736 | } |
| 3737 | |
| 3738 | /// Find a symbol by name in a specific scope (matches any symbol kind). |
| 3739 | export unsafe fn findSymbolInScope(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
| 3740 | return findInScope(scope, name, isAnySymbol); |
| 3741 | } |
| 3742 | |
| 3743 | /// Look up a value symbol by name, searching from the given scope outward. |
| 3744 | unsafe fn findValueSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
| 3745 | return findInScopeRecursive(scope, name, isValueSymbol); |
| 3746 | } |
| 3747 | |
| 3748 | /// Look up a type symbol by name, searching from the given scope outward. |
| 3749 | unsafe fn findTypeSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
| 3750 | return findInScopeRecursive(scope, name, isTypeSymbol); |
| 3751 | } |
| 3752 | |
| 3753 | /// Like `findValueSymbol`, but finds symbols of any kinds. |
| 3754 | unsafe fn findAnySymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
| 3755 | return findInScopeRecursive(scope, name, isAnySymbol); |
| 3756 | } |
| 3757 | |
| 3758 | /// Flatten an identifier or scope access chain into an array of name segments. |
| 3759 | /// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`. |
| 3760 | /// Return the number of segments written to the buffer. |
| 3761 | fn flattenPath 'arena ( |
| 3762 | self: &mut Resolver 'arena, |
| 3763 | node: *ast::Node, |
| 3764 | buf: &mut [*[u8]] |
| 3765 | ) -> u32 throws (ResolveError) { |
| 3766 | let mut out: u32 = 0; |
| 3767 | |
| 3768 | match node.value { |
| 3769 | case ast::NodeValue::Ident(name) if name.len > 0 => { |
| 3770 | assert buf.len >= 1, "flattenPath: invalid output buffer size"; |
| 3771 | set buf[0] = name; |
| 3772 | set out = 1; |
| 3773 | } |
| 3774 | case ast::NodeValue::ScopeAccess(access) => { |
| 3775 | // Recursively flatten parent path. |
| 3776 | let parent = try flattenPath(self, access.parent, buf); |
| 3777 | assert parent < buf.len, "flattenPath: invalid output buffer size"; |
| 3778 | let child = try nodeName(self, access.child); |
| 3779 | set buf[parent] = child; |
| 3780 | set out = parent + 1; |
| 3781 | } |
| 3782 | case ast::NodeValue::Super => { |
| 3783 | // `super` is handled by scope adjustment in `checkSuperAccess`. |
| 3784 | // Return empty prefix so the path continues from the next segment. |
| 3785 | set out = 0; |
| 3786 | return out; |
| 3787 | } |
| 3788 | else => { |
| 3789 | // Fallthrough to error. |
| 3790 | } |
| 3791 | } |
| 3792 | if out < 1 { |
| 3793 | throw emitError(self, node, ErrorKind::InvalidIdentifier(node)); |
| 3794 | } |
| 3795 | return out; |
| 3796 | } |
| 3797 | |
| 3798 | /// Find the module ID for a given scope by walking up the scope chain until |
| 3799 | /// we hit the module's scope. |
| 3800 | unsafe fn findModuleForScope(scope: *unsafe Scope) -> ?u16 { |
| 3801 | let mut s = scope; |
| 3802 | loop { |
| 3803 | if let id = s.moduleId { |
| 3804 | return id; |
| 3805 | } |
| 3806 | if let parent = s.parent { |
| 3807 | set s = parent; |
| 3808 | } else { |
| 3809 | return nil; |
| 3810 | } |
| 3811 | } |
| 3812 | } |
| 3813 | |
| 3814 | /// Return a retained module identity, if its ID is registered. |
| 3815 | export fn moduleFor 'arena (self: &Resolver 'arena, id: u16) -> ?*module::ModuleEntry { |
| 3816 | if id as u32 >= self.moduleEntries.len { |
| 3817 | return nil; |
| 3818 | } |
| 3819 | return self.moduleEntries[id as u32]; |
| 3820 | } |
| 3821 | |
| 3822 | /// Find a retained child identity by its parent and name. |
| 3823 | fn findChildModule 'arena (self: &Resolver 'arena, name: *[u8], parentId: u16) -> ?*module::ModuleEntry { |
| 3824 | for child in self.moduleEntries { |
| 3825 | if let entry = child { |
| 3826 | if entry.parent == parentId and mem::eq(entry.name, name) { |
| 3827 | return entry; |
| 3828 | } |
| 3829 | } |
| 3830 | } |
| 3831 | return nil; |
| 3832 | } |
| 3833 | |
| 3834 | /// Get the parent module scope for the current module. |
| 3835 | /// Returns the scope of the parent module, or `nil` if this is a root module. |
| 3836 | fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
| 3837 | let currentMod = moduleFor(self, self.currentMod) |
| 3838 | else throw emitError(self, node, ErrorKind::Internal); |
| 3839 | let parentId = currentMod.parent |
| 3840 | else return nil; // No parent module. |
| 3841 | |
| 3842 | return self.moduleScopes[parentId as u32]; |
| 3843 | } |
| 3844 | |
| 3845 | /// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`). |
| 3846 | /// Returns the parent scope and the original node so `flattenPath` can strip `super`. |
| 3847 | fn checkSuperAccess 'arena ( |
| 3848 | self: &mut Resolver 'arena, |
| 3849 | node: *ast::Node |
| 3850 | ) -> ?SuperAccessResult throws (ResolveError) { |
| 3851 | // TODO: Maybe we should deal with `super` after the path is flattened. |
| 3852 | if let case ast::NodeValue::ScopeAccess(access) = node.value { |
| 3853 | // Direct super access: `super::x`. |
| 3854 | if let case ast::NodeValue::Super = access.parent.value { |
| 3855 | let parentScope = try getParentModuleScope(self, node) |
| 3856 | else throw emitError(self, node, ErrorKind::InvalidModulePath); |
| 3857 | return SuperAccessResult { scope: parentScope, child: node }; |
| 3858 | } |
| 3859 | // Nested super access: `super::x::y`, check if parent path contains `super`. |
| 3860 | if let _ = try checkSuperAccess(self, access.parent) { |
| 3861 | let parentScope = try getParentModuleScope(self, node) |
| 3862 | else throw emitError(self, node, ErrorKind::InvalidModulePath); |
| 3863 | return SuperAccessResult { scope: parentScope, child: node }; |
| 3864 | } |
| 3865 | } |
| 3866 | return nil; |
| 3867 | } |
| 3868 | |
| 3869 | /// Check symbol visibility from declaration attributes and module identities. |
| 3870 | /// A symbol is accessible if: |
| 3871 | /// * It has the `export` attribute, OR |
| 3872 | /// * It's being accessed from within the module where it was defined. |
| 3873 | fn isSymbolVisible(attrs: u32, symModuleId: ?u16, currentModuleId: ?u16) -> bool { |
| 3874 | // Public symbols are visible from anywhere. |
| 3875 | if ast::hasAttribute(attrs, ast::Attribute::Export) { |
| 3876 | return true; |
| 3877 | } |
| 3878 | // In test mode, @test symbols are visible from anywhere |
| 3879 | // so the test runner can reference them. |
| 3880 | if ast::hasAttribute(attrs, ast::Attribute::Test) { |
| 3881 | return true; |
| 3882 | } |
| 3883 | // Private symbols are only visible from the same module. |
| 3884 | return symModuleId == currentModuleId; |
| 3885 | } |
| 3886 | |
| 3887 | /// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol, |
| 3888 | /// starting from the given scope. |
| 3889 | unsafe fn resolveAccess 'arena ( |
| 3890 | self: &mut Resolver 'arena, |
| 3891 | node: *ast::Node, |
| 3892 | access: ast::Access, |
| 3893 | scope: *unsafe Scope |
| 3894 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3895 | if let case ast::NodeValue::RegionApply { .. } = access.parent.value { |
| 3896 | let ty = try infer(self, access.parent); |
| 3897 | let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
| 3898 | try ensureNominalResolved(self, info, access.parent); |
| 3899 | let case NominalType::Union(body) = *info else throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
| 3900 | let name = try nodeName(self, access.child); |
| 3901 | let symbol = try resolveUnionVariantAccess(self, node, access, body, name); |
| 3902 | setNodeType(self, node, ty); |
| 3903 | return symbol; |
| 3904 | } |
| 3905 | // Handle `super` access by adjusting scope and node. |
| 3906 | let mut startScope = scope; |
| 3907 | let mut pathNode = node; |
| 3908 | if let superAccess = try checkSuperAccess(self, node) { |
| 3909 | set startScope = superAccess.scope; |
| 3910 | set pathNode = superAccess.child; |
| 3911 | } |
| 3912 | // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access, |
| 3913 | // while this function requires a scope access. |
| 3914 | let mut buffer: [*[u8]; 32] = undefined; |
| 3915 | let pathLen = try flattenPath(self, pathNode, &mut buffer[..]); |
| 3916 | |
| 3917 | return try resolvePath(self, node, access, &buffer[..pathLen], startScope); |
| 3918 | } |
| 3919 | |
| 3920 | /// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol, |
| 3921 | /// starting from the given scope. |
| 3922 | unsafe fn resolvePath 'arena ( |
| 3923 | self: &mut Resolver 'arena, |
| 3924 | node: *ast::Node, |
| 3925 | access: ast::Access, |
| 3926 | path: &[*[u8]], |
| 3927 | scope: *unsafe Scope |
| 3928 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 3929 | assert path.len <> 0, "resolvePath: empty path"; |
| 3930 | // Start by finding the root of the path. |
| 3931 | let root = path[0]; |
| 3932 | let sym = findInScopeRecursive(scope, root, isAnySymbol) |
| 3933 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root)); |
| 3934 | |
| 3935 | // Check visibility for symbol. |
| 3936 | if not isSymbolVisible(sym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) { |
| 3937 | throw emitError(self, node, ErrorKind::UnresolvedSymbol(root)); |
| 3938 | } |
| 3939 | // End condition. |
| 3940 | if path.len == 1 { |
| 3941 | return sym; |
| 3942 | } |
| 3943 | // Otherwise, we need to enter the next scope with the path suffix. |
| 3944 | match sym.data { |
| 3945 | case SymbolData::Module { scope, .. } => { |
| 3946 | return try resolvePath(self, node, access, &path[1..], scope); |
| 3947 | } |
| 3948 | case SymbolData::Type(ty) => { |
| 3949 | // Lazily resolve union body if not yet done. |
| 3950 | try ensureNominalResolved(self, ty, node); |
| 3951 | |
| 3952 | if let case NominalType::Union(unionType) = *ty { |
| 3953 | // TODO: Recurse with variant so we consolidate everything. |
| 3954 | if path.len > 2 { |
| 3955 | throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
| 3956 | } |
| 3957 | let variantName = path[1]; |
| 3958 | let variantSym = try resolveUnionVariantAccess( |
| 3959 | self, node, access, unionType, variantName |
| 3960 | ); |
| 3961 | // TODO: This shouldn't be here. |
| 3962 | setNodeType(self, node, Type::Nominal(ty)); |
| 3963 | return variantSym; |
| 3964 | } |
| 3965 | } |
| 3966 | else => {} // Fallthrough. |
| 3967 | } |
| 3968 | throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
| 3969 | } |
| 3970 | |
| 3971 | /// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope. |
| 3972 | /// This traverses the module hierarchy, checking visibility at each step. |
| 3973 | unsafe fn resolveModulePath 'arena ( |
| 3974 | self: &mut Resolver 'arena, |
| 3975 | module: *ast::Node |
| 3976 | ) -> ResolvedModule throws (ResolveError) { |
| 3977 | let mut startScope = self.scope; |
| 3978 | let mut pathNode = module; |
| 3979 | |
| 3980 | // Handle `super` access. |
| 3981 | if let superAccess = try checkSuperAccess(self, module) { |
| 3982 | set startScope = superAccess.scope; |
| 3983 | set pathNode = superAccess.child; |
| 3984 | } |
| 3985 | let mut pathBuf: [*[u8]; 16] = undefined; |
| 3986 | let pathLen = try flattenPath(self, pathNode, &mut pathBuf[..]); |
| 3987 | if pathLen == 0 { |
| 3988 | throw emitError(self, module, ErrorKind::UnresolvedSymbol("")); |
| 3989 | } |
| 3990 | let parentName = pathBuf[0]; |
| 3991 | |
| 3992 | // First, check if this is a sub-module of the start scope. |
| 3993 | if let sym = findSymbolInScope(startScope, parentName) { |
| 3994 | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
| 3995 | } |
| 3996 | // Not a sub-module, so look in the global scope for a package root. |
| 3997 | let sym = findSymbolInScope(self.pkgScope, parentName) |
| 3998 | else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName)); |
| 3999 | |
| 4000 | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
| 4001 | } |
| 4002 | |
| 4003 | /// Recursively resolve the remaining path segments by traversing child modules. |
| 4004 | unsafe fn resolveModulePathRecursive 'arena ( |
| 4005 | self: &mut Resolver 'arena, |
| 4006 | node: *ast::Node, |
| 4007 | path: &[*[u8]], |
| 4008 | sym: *unsafe Symbol |
| 4009 | ) -> ResolvedModule throws (ResolveError) { |
| 4010 | let case SymbolData::Module { entry, scope } = sym.data |
| 4011 | else throw emitError(self, node, ErrorKind::Internal); |
| 4012 | |
| 4013 | if path.len == 0 { |
| 4014 | return ResolvedModule { entry, scope }; |
| 4015 | } |
| 4016 | let childName = path[0]; |
| 4017 | let childSym = findSymbolInScope(scope, childName) |
| 4018 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName)); |
| 4019 | |
| 4020 | if not isSymbolVisible(childSym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) { |
| 4021 | throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName)); |
| 4022 | } |
| 4023 | return try resolveModulePathRecursive( |
| 4024 | self, |
| 4025 | node, |
| 4026 | &path[1..], |
| 4027 | childSym |
| 4028 | ); |
| 4029 | } |
| 4030 | |
| 4031 | /// Resolve a type name, which could be an identifier or scoped path. |
| 4032 | unsafe fn resolveTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) { |
| 4033 | match node.value { |
| 4034 | case ast::NodeValue::Ident(name) => { |
| 4035 | let sym = findTypeSymbol(self.scope, name) |
| 4036 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
| 4037 | let case SymbolData::Type(ty) = sym.data |
| 4038 | else throw emitError(self, node, ErrorKind::Internal); |
| 4039 | |
| 4040 | setNodeSymbol(self, node, sym); |
| 4041 | |
| 4042 | return ty; |
| 4043 | } |
| 4044 | case ast::NodeValue::ScopeAccess(access) => { |
| 4045 | let scope = self.scope; |
| 4046 | let sym = try resolveAccess(self, node, access, scope); |
| 4047 | let case SymbolData::Type(ty) = sym.data |
| 4048 | else throw emitError(self, node, ErrorKind::Internal); |
| 4049 | |
| 4050 | setNodeSymbol(self, node, sym); |
| 4051 | |
| 4052 | return ty; |
| 4053 | } |
| 4054 | else => panic "resolveTypeName: unsupported node value", |
| 4055 | } |
| 4056 | } |
| 4057 | |
| 4058 | /// Visit a top-level declaration in the declaration phase. |
| 4059 | /// This binds all names and analyzes signatures, types, and initializers. |
| 4060 | /// Function bodies are deferred to the definition phase. |
| 4061 | /// |
| 4062 | /// Nb. User-defined types are already handled by this point. |
| 4063 | unsafe fn visitDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
| 4064 | match node.value { |
| 4065 | case ast::NodeValue::FnDecl(_), |
| 4066 | ast::NodeValue::ConstDecl(_), |
| 4067 | ast::NodeValue::Mod(_), |
| 4068 | ast::NodeValue::Use(_) => { |
| 4069 | // Handled in previous passes. |
| 4070 | } |
| 4071 | case ast::NodeValue::StaticDecl(_) => { |
| 4072 | try infer(self, node); |
| 4073 | } |
| 4074 | case ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => { |
| 4075 | try resolveInstanceDecl(self, node, traitName, targetType, regions, methods); |
| 4076 | } |
| 4077 | case ast::NodeValue::MethodDecl { |
| 4078 | .. |
| 4079 | } => { |
| 4080 | try resolveMethodDecl(self, node); |
| 4081 | } |
| 4082 | else => { |
| 4083 | // Ignore non-declaration nodes. |
| 4084 | } |
| 4085 | } |
| 4086 | } |
| 4087 | |
| 4088 | /// Require an unsafe function or block. |
| 4089 | fn requireUnsafe 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
| 4090 | if not self.inUnsafeContext { |
| 4091 | throw emitError(self, node, ErrorKind::UnsafeOperation); |
| 4092 | } |
| 4093 | } |
| 4094 | |
| 4095 | /// Require an unsafe context for any access to an unsafe static. |
| 4096 | fn checkStaticAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, sym: &Symbol) |
| 4097 | throws (ResolveError) |
| 4098 | { |
| 4099 | if let case ast::NodeValue::StaticDecl(_) = sym.node.value { |
| 4100 | if ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) { |
| 4101 | try requireUnsafe(self, node); |
| 4102 | } |
| 4103 | } |
| 4104 | } |
| 4105 | |
| 4106 | /// Reject calls from safe code through unsafe function types. |
| 4107 | fn checkUnsafeCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: *FnType) |
| 4108 | throws (ResolveError) |
| 4109 | { |
| 4110 | if info.isUnsafe and not self.inUnsafeContext { |
| 4111 | throw emitError(self, node, ErrorKind::UnsafeCall); |
| 4112 | } |
| 4113 | } |
| 4114 | |
| 4115 | /// Visit a top-level definition, recursing into sub-modules. |
| 4116 | unsafe fn visitDef 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
| 4117 | match node.value { |
| 4118 | case ast::NodeValue::FnDecl(decl) => { |
| 4119 | try resolveFnDeclBody(self, node, decl) catch { |
| 4120 | return; |
| 4121 | }; |
| 4122 | } |
| 4123 | case ast::NodeValue::Mod(decl) => { |
| 4124 | let modName = try nodeName(self, decl.name); |
| 4125 | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
| 4126 | return; |
| 4127 | } |
| 4128 | let submod = try enterSubModule(self, modName, node); |
| 4129 | let case ast::NodeValue::Block(block) = submod.root.value |
| 4130 | else panic "visitDef: expected block for module root"; |
| 4131 | try resolveModuleDefs(self, &block) catch e { |
| 4132 | exitModuleScope(self, submod); |
| 4133 | throw e; |
| 4134 | }; |
| 4135 | exitModuleScope(self, submod); |
| 4136 | } |
| 4137 | case ast::NodeValue::RecordDecl(_), |
| 4138 | ast::NodeValue::UnionDecl(_), |
| 4139 | ast::NodeValue::Use(_), |
| 4140 | ast::NodeValue::TraitDecl { .. } => { |
| 4141 | // Skip: already analyzed in declaration phase. |
| 4142 | } |
| 4143 | case ast::NodeValue::InstanceDecl { methods, .. } => { |
| 4144 | try resolveInstanceMethodBodies(self, methods); |
| 4145 | } |
| 4146 | case ast::NodeValue::MethodDecl { |
| 4147 | .. |
| 4148 | } => { |
| 4149 | try resolveMethodBody(self, node); |
| 4150 | } |
| 4151 | else => { |
| 4152 | // FIXME: This allows module-level statements that should |
| 4153 | // normally only be valid inside function bodies. We currently |
| 4154 | // need this because of how tests are written, but it should |
| 4155 | // be eventually removed. |
| 4156 | try infer(self, node) catch { |
| 4157 | return; |
| 4158 | }; |
| 4159 | } |
| 4160 | } |
| 4161 | } |
| 4162 | |
| 4163 | /// Try to infer a node's type. |
| 4164 | unsafe fn infer 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
| 4165 | return try visit(self, node, Type::Unknown); |
| 4166 | } |
| 4167 | |
| 4168 | /// Permit named reference dependencies and direct call-scoped or local references. |
| 4169 | unsafe fn validateValueTypeReferences 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
| 4170 | throws (ResolveError) |
| 4171 | { |
| 4172 | try validateRegionDependencies(self, node, ty); |
| 4173 | if let case Type::Fn(info) = ty; info.regions == nil { |
| 4174 | return; |
| 4175 | } |
| 4176 | if isRefType(ty) { |
| 4177 | if let case Type::Pointer { target, .. } = ty { |
| 4178 | if containsUnscopedRef(*target) { |
| 4179 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4180 | } |
| 4181 | } else if let case Type::Slice { item, .. } = ty { |
| 4182 | if containsUnscopedRef(*item) { |
| 4183 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4184 | } |
| 4185 | } |
| 4186 | } else if containsUnscopedRef(ty) { |
| 4187 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4188 | } |
| 4189 | } |
| 4190 | |
| 4191 | /// Require a type that may be stored or escape a call. |
| 4192 | unsafe fn ensureStorableType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
| 4193 | throws (ResolveError) |
| 4194 | { |
| 4195 | try validateRegionDependencies(self, node, ty); |
| 4196 | if containsUnscopedRef(ty) { |
| 4197 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4198 | } |
| 4199 | } |
| 4200 | |
| 4201 | /// Resolve a type signature node. |
| 4202 | unsafe fn resolveValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
| 4203 | let ty = try visit(self, node, Type::Unknown); |
| 4204 | if let case Type::Nominal(info) = ty { |
| 4205 | try requireNominalArguments(self, info, node); |
| 4206 | } |
| 4207 | // Opaque value types are not allowed. |
| 4208 | if ty == Type::Opaque { |
| 4209 | throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed); |
| 4210 | } |
| 4211 | try validateValueTypeReferences(self, node, ty); |
| 4212 | return ty; |
| 4213 | } |
| 4214 | |
| 4215 | /// Analyze a node's type and check that it can be assigned to the expected type. |
| 4216 | unsafe fn checkAssignable 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type throws (ResolveError) { |
| 4217 | let actual = try visit(self, node, expected); |
| 4218 | let _ = try expectAssignable(self, expected, actual, node); |
| 4219 | if isRefType(expected) and isMutablePointerLike(expected) and isMutablePointerLike(actual) { |
| 4220 | if not try canMutateThrough(self, node) { |
| 4221 | throw emitError(self, node, ErrorKind::ImmutableBinding); |
| 4222 | } |
| 4223 | } |
| 4224 | return actual; |
| 4225 | } |
| 4226 | |
| 4227 | /// Analyze a node and propagate the resolved type. |
| 4228 | /// The `hint` parameter provides type context for inference and validation. |
| 4229 | /// When `nil`, the type must be inferred from the expression itself. |
| 4230 | unsafe fn visit 'arena (self: &mut Resolver 'arena, node: *ast::Node, hint: Type) -> Type |
| 4231 | throws (ResolveError) |
| 4232 | { |
| 4233 | if let ty = typeFor(self, node) { |
| 4234 | // An optional context completes a nil expression's storage type. |
| 4235 | if ty <> Type::Nil or not isOptionalType(hint) { |
| 4236 | return ty; |
| 4237 | } |
| 4238 | } |
| 4239 | match node.value { |
| 4240 | case ast::NodeValue::Ident(name) => { |
| 4241 | let sym = findAnySymbol(self.scope, name) |
| 4242 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
| 4243 | try checkStaticAccess(self, node, sym); |
| 4244 | setNodeSymbol(self, node, sym); |
| 4245 | match sym.data { |
| 4246 | case SymbolData::Value { type, .. } => |
| 4247 | return setNodeType(self, node, type), |
| 4248 | case SymbolData::Constant { type, value } => { |
| 4249 | if let val = value { |
| 4250 | setNodeConstValue(self, node, val); |
| 4251 | } |
| 4252 | return setNodeType(self, node, type); |
| 4253 | }, |
| 4254 | case SymbolData::Type(t) => |
| 4255 | return setNodeType(self, node, Type::Nominal(hintedNominal(t, hint))), |
| 4256 | case SymbolData::Variant { .. } => |
| 4257 | return Type::Void, |
| 4258 | case SymbolData::Module { .. } => |
| 4259 | throw emitError(self, node, ErrorKind::UnexpectedModuleName), |
| 4260 | case SymbolData::Trait(_) => |
| 4261 | throw emitError(self, node, ErrorKind::UnexpectedTraitName), |
| 4262 | } |
| 4263 | }, |
| 4264 | case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal, hint), |
| 4265 | case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access), |
| 4266 | case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop), |
| 4267 | case ast::NodeValue::RegionBlock { region, bindings, body, isSession } => { |
| 4268 | if isSession { |
| 4269 | return try resolveSessionBlock(self, node, region, bindings, body); |
| 4270 | } |
| 4271 | return try resolveBorrowBlock(self, node, region, bindings, body); |
| 4272 | } |
| 4273 | case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block), |
| 4274 | case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl), |
| 4275 | case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic( |
| 4276 | self, node, decl.ident, decl.type, decl.value, decl.attrs, true |
| 4277 | ), |
| 4278 | case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic( |
| 4279 | self, node, decl.ident, decl.type, decl.value, decl.attrs, false |
| 4280 | ), |
| 4281 | case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param), |
| 4282 | case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond), |
| 4283 | case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond, hint), |
| 4284 | case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond), |
| 4285 | case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode), |
| 4286 | case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode), |
| 4287 | case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode), |
| 4288 | case ast::NodeValue::Loop { body } => { |
| 4289 | let loopType = try visitLoop(self, body); |
| 4290 | return setNodeType(self, node, loopType); |
| 4291 | }, |
| 4292 | case ast::NodeValue::Break, ast::NodeValue::Continue => return try resolveLoopControl(self, node), |
| 4293 | case ast::NodeValue::Match(sw) => return try resolveMatch(self, node, sw), |
| 4294 | case ast::NodeValue::MatchProng(_) => panic "visit: `MatchProng` not handled here", |
| 4295 | case ast::NodeValue::LetElse(letElse) => return try resolveLetElse(self, node, letElse), |
| 4296 | case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, args), |
| 4297 | case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign), |
| 4298 | case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint), |
| 4299 | case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint), |
| 4300 | case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint), |
| 4301 | case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index), |
| 4302 | case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access, hint), |
| 4303 | case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint), |
| 4304 | case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint), |
| 4305 | case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr), |
| 4306 | case ast::NodeValue::Range(range) => return try resolveRange(self, node, range), |
| 4307 | case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint), |
| 4308 | case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value), |
| 4309 | case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr), |
| 4310 | case ast::NodeValue::Panic { message } => { |
| 4311 | try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type. |
| 4312 | class: types::PointerClass::Owned, |
| 4313 | item: allocType(self, Type::U8), |
| 4314 | mutable: false, |
| 4315 | }); |
| 4316 | return setNodeType(self, node, Type::Never); |
| 4317 | }, |
| 4318 | case ast::NodeValue::Assert { condition, message } => { |
| 4319 | try visit(self, condition, Type::Bool); |
| 4320 | try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type. |
| 4321 | class: types::PointerClass::Owned, |
| 4322 | item: allocType(self, Type::U8), |
| 4323 | mutable: false, |
| 4324 | }); |
| 4325 | return setNodeType(self, node, Type::Void); |
| 4326 | }, |
| 4327 | case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop), |
| 4328 | case ast::NodeValue::ExprStmt(expr) => { |
| 4329 | // Pass `Void` as expected type to indicate value is discarded. |
| 4330 | let exprTy = try visit(self, expr, Type::Void); |
| 4331 | return setNodeType(self, node, Type::Never if exprTy == Type::Never else Type::Void); |
| 4332 | }, |
| 4333 | case ast::NodeValue::RegionApply { value, regions } => |
| 4334 | return try resolveRegionApply(self, node, value, regions), |
| 4335 | case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig), |
| 4336 | case ast::NodeValue::Super => { |
| 4337 | // `super` by itself is invalid, must be used in scope access. |
| 4338 | throw emitError(self, node, ErrorKind::InvalidModulePath); |
| 4339 | }, |
| 4340 | case ast::NodeValue::Nil => { |
| 4341 | // Use the hint type if it's an optional, otherwise fall back to `Nil`. |
| 4342 | if let case Type::Optional(_) = hint { |
| 4343 | return setNodeType(self, node, hint); |
| 4344 | } |
| 4345 | return setNodeType(self, node, Type::Nil); |
| 4346 | }, |
| 4347 | case ast::NodeValue::Undef => { |
| 4348 | try requireUnsafe(self, node); |
| 4349 | return setNodeType(self, node, Type::Undefined); |
| 4350 | }, |
| 4351 | case ast::NodeValue::Bool(value) => { |
| 4352 | setNodeConstValue(self, node, ConstValue::Bool(value)); |
| 4353 | return setNodeType(self, node, Type::Bool); |
| 4354 | } |
| 4355 | case ast::NodeValue::Char(value) => { |
| 4356 | setNodeConstValue(self, node, ConstValue::Char(value)); |
| 4357 | return setNodeType(self, node, Type::U8); |
| 4358 | } |
| 4359 | case ast::NodeValue::String(text) => { |
| 4360 | setNodeConstValue(self, node, ConstValue::String(text)); |
| 4361 | let byteTy = allocType(self, Type::U8); |
| 4362 | let sliceTy = allocType(self, Type::Slice { |
| 4363 | class: types::PointerClass::Owned, |
| 4364 | item: byteTy, |
| 4365 | mutable: false, |
| 4366 | }); |
| 4367 | return setNodeType(self, node, *sliceTy); |
| 4368 | }, |
| 4369 | case ast::NodeValue::Number(lit) => { |
| 4370 | setNodeConstValue(self, node, ConstValue::Int(ConstInt { |
| 4371 | magnitude: lit.magnitude, |
| 4372 | bits: 64, |
| 4373 | signed: false, |
| 4374 | negative: false, |
| 4375 | })); |
| 4376 | return setNodeType(self, node, Type::Int); |
| 4377 | }, |
| 4378 | case ast::NodeValue::Placeholder => { |
| 4379 | throw emitError(self, node, ErrorKind::PlaceholderExpression); |
| 4380 | }, |
| 4381 | else => { |
| 4382 | throw emitError(self, node, ErrorKind::UnexpectedNode(node)); |
| 4383 | } |
| 4384 | } |
| 4385 | } |
| 4386 | |
| 4387 | /// Visit an optional node when present. |
| 4388 | unsafe fn visitOptional 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, hint: Type) -> ?Type |
| 4389 | throws (ResolveError) |
| 4390 | { |
| 4391 | if let n = node { |
| 4392 | return try visit(self, n, hint); |
| 4393 | } |
| 4394 | return nil; |
| 4395 | } |
| 4396 | |
| 4397 | /// Visit every node contained in a list, returning the last resolved type. |
| 4398 | unsafe fn visitList 'arena (self: &mut Resolver 'arena, list: *[*ast::Node]) -> Type |
| 4399 | throws (ResolveError) |
| 4400 | { |
| 4401 | let mut diverges = false; |
| 4402 | for item in list { |
| 4403 | if try infer(self, item) == Type::Never { |
| 4404 | set diverges = true; |
| 4405 | } |
| 4406 | } |
| 4407 | if diverges { |
| 4408 | return Type::Never; |
| 4409 | } |
| 4410 | return Type::Void; |
| 4411 | } |
| 4412 | |
| 4413 | /// Collect attribute flags applied to a declaration. |
| 4414 | fn resolveAttributes(attrs: ?ast::Attributes) -> u32 { |
| 4415 | let list = attrs else return 0; |
| 4416 | let mut mask: u32 = 0; |
| 4417 | |
| 4418 | for node in list.list { |
| 4419 | let case ast::NodeValue::Attribute(attr) = node.value |
| 4420 | else panic "resolveAttributes: invalid attribute node"; |
| 4421 | set mask |= (attr as u32); |
| 4422 | } |
| 4423 | return mask; |
| 4424 | } |
| 4425 | |
| 4426 | /// Ensure the `default` attribute is only applied to functions. |
| 4427 | fn ensureDefaultAttrNotAllowed 'arena (self: &mut Resolver 'arena, node: *ast::Node, attrs: u32) |
| 4428 | throws (ResolveError) |
| 4429 | { |
| 4430 | let defaultBit = ast::Attribute::Default as u32; |
| 4431 | if (attrs & defaultBit) <> 0 { |
| 4432 | throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn); |
| 4433 | } |
| 4434 | } |
| 4435 | |
| 4436 | /// Analyze a block node, allocating a nested lexical scope. |
| 4437 | unsafe fn resolveBlock 'arena (self: &mut Resolver 'arena, node: *ast::Node, block: ast::Block) -> Type |
| 4438 | throws (ResolveError) |
| 4439 | { |
| 4440 | enterScope(self, node); |
| 4441 | let wasUnsafe = self.inUnsafeContext; |
| 4442 | set self.inUnsafeContext = wasUnsafe or block.isUnsafe; |
| 4443 | let blockTy = try visitList(self, block.statements) catch { |
| 4444 | // One of the statements in the block failed analysis. We simply proceed |
| 4445 | // without checking the rest of the block statements. Return `Never` to |
| 4446 | // avoid spurious `FnMissingReturn` errors. |
| 4447 | exitScope(self); |
| 4448 | set self.inUnsafeContext = wasUnsafe; |
| 4449 | return setNodeType(self, node, Type::Never); |
| 4450 | }; |
| 4451 | exitScope(self); |
| 4452 | set self.inUnsafeContext = wasUnsafe; |
| 4453 | |
| 4454 | return setNodeType(self, node, blockTy); |
| 4455 | } |
| 4456 | |
| 4457 | /// Introduce a concrete region under an explicit parent or enclosing region block. |
| 4458 | unsafe fn borrowRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *RegionScope throws (ResolveError) { |
| 4459 | let case ast::NodeValue::Region { name, parent } = node.value |
| 4460 | else panic "borrowRegion: invalid region node"; |
| 4461 | if findRegion(self.regionScope, name) <> nil { |
| 4462 | throw emitError(self, node, ErrorKind::DuplicateBinding(name)); |
| 4463 | } |
| 4464 | let mut enclosing: ?*unsafe types::Region = nil; |
| 4465 | let mut current = self.regionScope; |
| 4466 | while let scope = current { |
| 4467 | for region in scope.entries { |
| 4468 | if region.origin == types::RegionOrigin::Block { |
| 4469 | set enclosing = region; |
| 4470 | break; |
| 4471 | } |
| 4472 | } |
| 4473 | if enclosing <> nil { |
| 4474 | break; |
| 4475 | } |
| 4476 | set current = scope.parent; |
| 4477 | } |
| 4478 | if let parentNode = parent { |
| 4479 | set enclosing = try resolveRegion(self, parentNode); |
| 4480 | } |
| 4481 | let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region)) |
| 4482 | as *unsafe mut types::Region; |
| 4483 | set *region = types::Region { id: node.id, origin: types::RegionOrigin::Block, name, parent: enclosing }; |
| 4484 | let entries = try! alloc::allocRawSlice( |
| 4485 | self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1 |
| 4486 | ) as *unsafe mut [*unsafe mut types::Region]; |
| 4487 | set entries[0] = region; |
| 4488 | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
| 4489 | set *scope = RegionScope { |
| 4490 | declarations: RegionDeclarations::Block(node), |
| 4491 | entries, |
| 4492 | parent: self.regionScope, |
| 4493 | }; |
| 4494 | return scope; |
| 4495 | } |
| 4496 | |
| 4497 | /// Qualify an existing-place borrow with its block's region. |
| 4498 | /// |
| 4499 | /// A permission cell loan deliberately joins independent storage and |
| 4500 | /// permission regions. Its paired authority witness proves the lexical |
| 4501 | /// shortening that the single-parent region graph cannot represent. |
| 4502 | unsafe fn qualifyBlockBorrow 'arena ( |
| 4503 | self: &mut Resolver 'arena, node: *ast::Node, ty: Type, |
| 4504 | region: *unsafe types::Region, hasPermissionWitness: bool |
| 4505 | ) -> Type throws (ResolveError) { |
| 4506 | let mut class = types::PointerClass::Ref; |
| 4507 | match ty { |
| 4508 | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
| 4509 | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
| 4510 | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
| 4511 | else => throw emitError(self, node, ErrorKind::RefBinding), |
| 4512 | } |
| 4513 | if let case types::PointerClass::Region(source) = class { |
| 4514 | if not hasPermissionWitness and not types::regionContains(source, region) { |
| 4515 | throw emitError(self, node, ErrorKind::RegionParent(region.name)); |
| 4516 | } |
| 4517 | } |
| 4518 | match ty { |
| 4519 | case Type::Cell { permission, payload, .. } => |
| 4520 | return Type::Cell { |
| 4521 | class: types::PointerClass::Region(region), permission, payload, |
| 4522 | }, |
| 4523 | case Type::Pointer { target, mutable, .. } => |
| 4524 | return Type::Pointer { class: types::PointerClass::Region(region), target, mutable }, |
| 4525 | case Type::Slice { item, mutable, .. } => |
| 4526 | return Type::Slice { class: types::PointerClass::Region(region), item, mutable }, |
| 4527 | else => throw emitError(self, node, ErrorKind::RefBinding), |
| 4528 | } |
| 4529 | } |
| 4530 | |
| 4531 | /// Check source places before publishing the region's bindings to its body. |
| 4532 | unsafe fn resolveBorrowBlock 'arena ( |
| 4533 | self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node, |
| 4534 | bindings: *[*ast::Node], body: *ast::Node |
| 4535 | ) -> Type throws (ResolveError) { |
| 4536 | if self.currentFn == nil { |
| 4537 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4538 | } |
| 4539 | let scope = try borrowRegion(self, regionNode); |
| 4540 | let region = scope.entries[0]; |
| 4541 | let mut authorityPermissions: [?*unsafe types::Region; MAX_REGIONAL_LOANS] = |
| 4542 | [nil; MAX_REGIONAL_LOANS]; |
| 4543 | let mut authorityExclusive: [bool; MAX_REGIONAL_LOANS] = |
| 4544 | [false; MAX_REGIONAL_LOANS]; |
| 4545 | let mut authorityLen: u32 = 0; |
| 4546 | let mut payloadPermissions: [?*unsafe types::Region; MAX_REGIONAL_LOANS] = |
| 4547 | [nil; MAX_REGIONAL_LOANS]; |
| 4548 | let mut payloadExclusive: [bool; MAX_REGIONAL_LOANS] = |
| 4549 | [false; MAX_REGIONAL_LOANS]; |
| 4550 | let mut payloadLen: u32 = 0; |
| 4551 | for bindingNode in bindings { |
| 4552 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 4553 | else panic "resolveBorrowBlock: invalid binding node"; |
| 4554 | let case ast::NodeValue::AddressOf(address) = binding.value.value |
| 4555 | else panic "resolveBorrowBlock: invalid source node"; |
| 4556 | try infer(self, binding.value); |
| 4557 | if not ast::isPlaceExpr(address.target) or borrowPlace(self, address.target).root == nil { |
| 4558 | throw emitError(self, binding.value, ErrorKind::RefBinding); |
| 4559 | } |
| 4560 | if address.kind == ast::AddressKind::Cell { |
| 4561 | continue; |
| 4562 | } |
| 4563 | if let cellTy = try inferCellPayload(self, address.target) { |
| 4564 | if let case Type::Cell { permission: controlled, .. } = cellTy { |
| 4565 | if let permission = controlled { |
| 4566 | if payloadLen >= MAX_REGIONAL_LOANS { |
| 4567 | throw emitError(self, binding.value, ErrorKind::RegionalLoanOverflow); |
| 4568 | } |
| 4569 | set payloadPermissions[payloadLen] = permission; |
| 4570 | set payloadExclusive[payloadLen] = |
| 4571 | address.kind == ast::AddressKind::Mutable; |
| 4572 | set payloadLen += 1; |
| 4573 | continue; |
| 4574 | } |
| 4575 | } |
| 4576 | } |
| 4577 | if let case ast::NodeValue::Deref(source) = address.target.value { |
| 4578 | let sourceTy = try infer(self, source); |
| 4579 | if let case Type::Pointer { |
| 4580 | class: types::PointerClass::Region(permission), |
| 4581 | mutable: true, |
| 4582 | .. |
| 4583 | } = sourceTy { |
| 4584 | if authorityLen >= MAX_REGIONAL_LOANS { |
| 4585 | throw emitError(self, binding.value, ErrorKind::RegionalLoanOverflow); |
| 4586 | } |
| 4587 | set authorityPermissions[authorityLen] = permission; |
| 4588 | set authorityExclusive[authorityLen] = |
| 4589 | address.kind == ast::AddressKind::Mutable; |
| 4590 | set authorityLen += 1; |
| 4591 | } |
| 4592 | } |
| 4593 | } |
| 4594 | for bindingNode in bindings { |
| 4595 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 4596 | else panic "resolveBorrowBlock: invalid binding node"; |
| 4597 | let case ast::NodeValue::AddressOf(address) = binding.value.value |
| 4598 | else panic "resolveBorrowBlock: invalid source node"; |
| 4599 | let mut permission: ?*unsafe types::Region = nil; |
| 4600 | let mut isPayload = false; |
| 4601 | if address.kind <> ast::AddressKind::Cell { |
| 4602 | if let cellTy = try inferCellPayload(self, address.target) { |
| 4603 | if let case Type::Cell { permission: controlled, .. } = cellTy { |
| 4604 | set permission = controlled; |
| 4605 | set isPayload = controlled <> nil; |
| 4606 | } |
| 4607 | } |
| 4608 | if permission == nil { |
| 4609 | if let case ast::NodeValue::Deref(source) = address.target.value { |
| 4610 | let sourceTy = try infer(self, source); |
| 4611 | if let case Type::Pointer { |
| 4612 | class: types::PointerClass::Region(controlled), |
| 4613 | mutable: true, |
| 4614 | .. |
| 4615 | } = sourceTy { |
| 4616 | set permission = controlled; |
| 4617 | } |
| 4618 | } |
| 4619 | } |
| 4620 | } |
| 4621 | let mut hasPermissionWitness = false; |
| 4622 | if let controlled = permission { |
| 4623 | let exclusive = address.kind == ast::AddressKind::Mutable; |
| 4624 | if isPayload { |
| 4625 | for i in 0..authorityLen { |
| 4626 | let candidate = authorityPermissions[i] |
| 4627 | else panic "resolveBorrowBlock: missing authority permission"; |
| 4628 | if candidate.id == controlled.id and authorityExclusive[i] == exclusive { |
| 4629 | set hasPermissionWitness = true; |
| 4630 | break; |
| 4631 | } |
| 4632 | } |
| 4633 | } else { |
| 4634 | for i in 0..payloadLen { |
| 4635 | let candidate = payloadPermissions[i] |
| 4636 | else panic "resolveBorrowBlock: missing payload permission"; |
| 4637 | if candidate.id == controlled.id and payloadExclusive[i] == exclusive { |
| 4638 | set hasPermissionWitness = true; |
| 4639 | break; |
| 4640 | } |
| 4641 | } |
| 4642 | } |
| 4643 | } |
| 4644 | let ty = try infer(self, binding.value); |
| 4645 | let qualified = try qualifyBlockBorrow( |
| 4646 | self, binding.value, ty, region, hasPermissionWitness, |
| 4647 | ); |
| 4648 | setNodeType(self, binding.value, qualified); |
| 4649 | } |
| 4650 | let previous = self.regionScope; |
| 4651 | set self.regionScope = scope; |
| 4652 | set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope); |
| 4653 | enterScope(self, node); |
| 4654 | let result = try resolveBorrowBody(self, bindings, body) catch error { |
| 4655 | exitScope(self); |
| 4656 | set self.regionScope = previous; |
| 4657 | throw error; |
| 4658 | }; |
| 4659 | exitScope(self); |
| 4660 | set self.regionScope = previous; |
| 4661 | return setNodeType(self, node, result); |
| 4662 | } |
| 4663 | |
| 4664 | /// Bind a checked region's source references and resolve its statement body. |
| 4665 | unsafe fn resolveBorrowBody 'arena (self: &mut Resolver 'arena, bindings: *[*ast::Node], body: *ast::Node) -> Type |
| 4666 | throws (ResolveError) |
| 4667 | { |
| 4668 | for bindingNode in bindings { |
| 4669 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 4670 | else panic "resolveBorrowBody: invalid binding node"; |
| 4671 | try resolveLet(self, bindingNode, ast::borrowBinding(binding)); |
| 4672 | } |
| 4673 | return try infer(self, body); |
| 4674 | } |
| 4675 | |
| 4676 | /// Find a declaration by spelling when the name is not interned. |
| 4677 | unsafe fn findSpelledSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
| 4678 | for i in 0..scope.symbolsLen { |
| 4679 | let symbol = scope.symbols[i]; |
| 4680 | if mem::eq(symbol.name, name) { |
| 4681 | return symbol; |
| 4682 | } |
| 4683 | } |
| 4684 | return nil; |
| 4685 | } |
| 4686 | |
| 4687 | /// Look up a compiler-known declaration in the standard allocation module. |
| 4688 | unsafe fn allocationSymbol 'arena (self: &Resolver 'arena, name: *[u8]) -> ?*unsafe mut Symbol { |
| 4689 | let mut scope = self.pkgScope; |
| 4690 | for segment in ["std", "lang", "alloc"] { |
| 4691 | let symbol = findSpelledSymbol(scope, segment) else return nil; |
| 4692 | let case SymbolData::Module { scope: child, .. } = symbol.data else return nil; |
| 4693 | set scope = child; |
| 4694 | } |
| 4695 | return findSpelledSymbol(scope, name); |
| 4696 | } |
| 4697 | |
| 4698 | /// Bind an allocation interface while retaining the source arena until region exit. |
| 4699 | unsafe fn resolveSessionBlock 'arena ( |
| 4700 | self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node, |
| 4701 | bindings: *[*ast::Node], body: *ast::Node |
| 4702 | ) -> Type throws (ResolveError) { |
| 4703 | if self.currentFn == nil or bindings.len <> 1 { |
| 4704 | throw emitError(self, node, ErrorKind::InvalidSessionSource); |
| 4705 | } |
| 4706 | let bindingNode = bindings[0]; |
| 4707 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 4708 | else panic "resolveSessionBlock: invalid binding"; |
| 4709 | let case ast::NodeValue::AddressOf(address) = binding.value.value |
| 4710 | else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
| 4711 | let ty = try infer(self, binding.value); |
| 4712 | let case Type::Pointer { target, .. } = ty |
| 4713 | else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
| 4714 | let allocTrait = allocationSymbol(self, "Alloc") |
| 4715 | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
| 4716 | let case SymbolData::Trait(allocInfo) = allocTrait.data |
| 4717 | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
| 4718 | let allocModule = moduleIdForSymbol(self, allocTrait) |
| 4719 | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
| 4720 | let mut allocInstance: ?*unsafe InstanceEntry = nil; |
| 4721 | for i in 0..self.instancesLen { |
| 4722 | let candidate: *unsafe InstanceEntry = &self.instances[i]; |
| 4723 | if candidate.traitType.moduleId == allocModule and mem::eq(candidate.traitType.name, allocInfo.name) |
| 4724 | and erasedTypesEqual(candidate.concreteType, *target) |
| 4725 | { |
| 4726 | set allocInstance = candidate; |
| 4727 | break; |
| 4728 | } |
| 4729 | } |
| 4730 | let selected = allocInstance |
| 4731 | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
| 4732 | if address.kind <> ast::AddressKind::Mutable or not ast::isPlaceExpr(address.target) |
| 4733 | or borrowPlace(self, address.target).root == nil |
| 4734 | { |
| 4735 | throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
| 4736 | } |
| 4737 | let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject { |
| 4738 | traitInfo: allocInfo, inst: selected, |
| 4739 | }); |
| 4740 | let scope = try borrowRegion(self, regionNode); |
| 4741 | let region = scope.entries[0]; |
| 4742 | if let sourceRegion = referenceRegion(ty) { |
| 4743 | set region.parent = sourceRegion; |
| 4744 | } |
| 4745 | setNodeType(self, binding.value, try qualifyBlockBorrow(self, binding.value, ty, region, false)); |
| 4746 | let previous = self.regionScope; |
| 4747 | set self.regionScope = scope; |
| 4748 | set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope); |
| 4749 | enterScope(self, node); |
| 4750 | let result = try resolveSessionBody(self, bindingNode, binding, region, body) catch error { |
| 4751 | exitScope(self); |
| 4752 | set self.regionScope = previous; |
| 4753 | throw error; |
| 4754 | }; |
| 4755 | exitScope(self); |
| 4756 | set self.regionScope = previous; |
| 4757 | return setNodeType(self, node, result); |
| 4758 | } |
| 4759 | |
| 4760 | /// Introduce the opaque session value and check its body. |
| 4761 | unsafe fn resolveSessionBody 'arena ( |
| 4762 | self: &mut Resolver 'arena, node: *ast::Node, binding: ast::Arg, |
| 4763 | region: *unsafe types::Region, body: *ast::Node |
| 4764 | ) -> Type throws (ResolveError) { |
| 4765 | let ident = binding.label else panic "resolveSessionBody: missing binding name"; |
| 4766 | let _ = try bindValueIdent(self, ident, node, Type::Session(region), false, 0, 0); |
| 4767 | return try infer(self, body); |
| 4768 | } |
| 4769 | |
| 4770 | /// Analyze a `let` declaration and bind its identifier. |
| 4771 | unsafe fn resolveLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Let) -> Type |
| 4772 | throws (ResolveError) |
| 4773 | { |
| 4774 | let mut alignment: u32 = 0; // Zero is default. |
| 4775 | let mut bindingTy = Type::Unknown; |
| 4776 | let mut valueTy = Type::Unknown; |
| 4777 | |
| 4778 | // Check type. |
| 4779 | if let declTy = try visitOptional(self, decl.type, Type::Unknown) { |
| 4780 | set valueTy = try checkAssignable(self, decl.value, declTy); |
| 4781 | set bindingTy = declTy; |
| 4782 | } else { |
| 4783 | set bindingTy = try infer(self, decl.value); |
| 4784 | set valueTy = bindingTy; |
| 4785 | |
| 4786 | if not isTypeInferrable(bindingTy) { |
| 4787 | throw emitError(self, decl.value, ErrorKind::CannotInferType); |
| 4788 | } |
| 4789 | } |
| 4790 | try validateValueTypeReferences(self, node, bindingTy); |
| 4791 | if isRefType(bindingTy) { |
| 4792 | if self.currentFn == nil { |
| 4793 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 4794 | } |
| 4795 | if decl.mutable and (referenceRegion(bindingTy) == nil or not isCopy(bindingTy)) { |
| 4796 | throw emitError(self, node, ErrorKind::RefBinding); |
| 4797 | } |
| 4798 | } |
| 4799 | // Variables cannot have void type. |
| 4800 | if bindingTy == Type::Void { |
| 4801 | throw emitError(self, decl.value, ErrorKind::CannotAssignVoid); |
| 4802 | } |
| 4803 | // Variables cannot have opaque type directly. |
| 4804 | if bindingTy == Type::Opaque { |
| 4805 | throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed); |
| 4806 | } |
| 4807 | // Check alignment. |
| 4808 | if let a = decl.alignment { |
| 4809 | let case ast::NodeValue::Align { value } = a.value |
| 4810 | else panic "resolveLet: expected Align node"; |
| 4811 | set alignment = try checkSizeInt(self, value); |
| 4812 | } |
| 4813 | assert bindingTy <> Type::Unknown; |
| 4814 | |
| 4815 | // Alignment must be zero or a power of two. |
| 4816 | if alignment <> 0 and (alignment & (alignment - 1)) <> 0 { |
| 4817 | throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment)); |
| 4818 | } |
| 4819 | let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0); |
| 4820 | |
| 4821 | // Untyped initializers use the declared storage type. |
| 4822 | if not isTypeInferrable(valueTy) { |
| 4823 | setNodeType(self, decl.value, bindingTy); |
| 4824 | } |
| 4825 | |
| 4826 | return Type::Never if valueTy == Type::Never else Type::Void; |
| 4827 | } |
| 4828 | |
| 4829 | /// Check whether a node is an integer literal, optionally under unary negation. |
| 4830 | fn isIntegerLiteralExpr(node: *ast::Node) -> bool { |
| 4831 | match node.value { |
| 4832 | case ast::NodeValue::Number(_) => return true, |
| 4833 | case ast::NodeValue::UnOp(unop) => { |
| 4834 | if unop.op == ast::UnaryOp::Neg { |
| 4835 | return isIntegerLiteralExpr(unop.value); |
| 4836 | } |
| 4837 | return false; |
| 4838 | }, |
| 4839 | else => return false, |
| 4840 | } |
| 4841 | } |
| 4842 | |
| 4843 | /// Determine whether a node represents a compile-time constant expression. |
| 4844 | export unsafe fn isConstExpr 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
| 4845 | match node.value { |
| 4846 | case ast::NodeValue::Bool(_), |
| 4847 | ast::NodeValue::Char(_), |
| 4848 | ast::NodeValue::Number(_), |
| 4849 | ast::NodeValue::String(_), |
| 4850 | ast::NodeValue::Undef, |
| 4851 | ast::NodeValue::Nil => { |
| 4852 | return true; |
| 4853 | }, |
| 4854 | case ast::NodeValue::ArrayLit(items) => { |
| 4855 | for item in items { |
| 4856 | if not isConstExpr(self, item) { |
| 4857 | return false; |
| 4858 | } |
| 4859 | } |
| 4860 | return true; |
| 4861 | }, |
| 4862 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
| 4863 | return isConstExpr(self, repeat.item); |
| 4864 | }, |
| 4865 | case ast::NodeValue::AddressOf(addr) => { |
| 4866 | let ty = typeFor(self, node) else { |
| 4867 | return false; |
| 4868 | }; |
| 4869 | if let case Type::Slice { .. } = ty { |
| 4870 | return isConstExpr(self, addr.target); |
| 4871 | } |
| 4872 | return false; |
| 4873 | }, |
| 4874 | case ast::NodeValue::RecordLit(lit) => { |
| 4875 | // Record literals are constant if all field values are constant. |
| 4876 | for field in lit.fields { |
| 4877 | if let case ast::NodeValue::RecordLitField(fieldLit) = field.value { |
| 4878 | if not isConstExpr(self, fieldLit.value) { |
| 4879 | return false; |
| 4880 | } |
| 4881 | } |
| 4882 | } |
| 4883 | return true; |
| 4884 | }, |
| 4885 | case ast::NodeValue::Ident(_), |
| 4886 | ast::NodeValue::ScopeAccess(_) => { |
| 4887 | // Identifiers and scope accesses referencing constants, union |
| 4888 | // variants, or function values are constant expressions. |
| 4889 | if let sym = symbolFor(self, node) { |
| 4890 | match sym.data { |
| 4891 | case SymbolData::Variant { .. }, |
| 4892 | SymbolData::Constant { .. } => return true, |
| 4893 | case SymbolData::Value { type, .. } => { |
| 4894 | if let case Type::Fn(_) = type { |
| 4895 | return true; |
| 4896 | } |
| 4897 | } |
| 4898 | else => {} |
| 4899 | } |
| 4900 | } |
| 4901 | return false; |
| 4902 | }, |
| 4903 | case ast::NodeValue::Call(call) => { |
| 4904 | // Constructor calls (union variants, unlabeled records) are constant |
| 4905 | // if all payload args are themselves constant. |
| 4906 | if let sym = symbolFor(self, call.callee) { |
| 4907 | match sym.data { |
| 4908 | case SymbolData::Variant { .. } => {} |
| 4909 | case SymbolData::Type(NominalType::Record(recInfo)) => { |
| 4910 | if recInfo.labeled { |
| 4911 | return false; |
| 4912 | } |
| 4913 | }, |
| 4914 | else => return false, |
| 4915 | } |
| 4916 | for arg in call.args { |
| 4917 | if not isConstExpr(self, arg) { |
| 4918 | return false; |
| 4919 | } |
| 4920 | } |
| 4921 | return true; |
| 4922 | } |
| 4923 | return false; |
| 4924 | }, |
| 4925 | case ast::NodeValue::BinOp(binop) => { |
| 4926 | // Binary expressions are constant if both operands are constant. |
| 4927 | return isConstExpr(self, binop.left) and isConstExpr(self, binop.right); |
| 4928 | }, |
| 4929 | case ast::NodeValue::UnOp(unop) => { |
| 4930 | // Unary expressions are constant if the operand is constant. |
| 4931 | return isConstExpr(self, unop.value); |
| 4932 | }, |
| 4933 | case ast::NodeValue::As(expr) => { |
| 4934 | // Cast expressions are constant if the source value is constant. |
| 4935 | return isConstExpr(self, expr.value); |
| 4936 | }, |
| 4937 | else => { |
| 4938 | return false; |
| 4939 | } |
| 4940 | } |
| 4941 | } |
| 4942 | |
| 4943 | /// Construct an integer constant descriptor. |
| 4944 | fn constInt(magnitude: u64, bits: u8, signed: bool, negative: bool) -> ConstValue { |
| 4945 | return ConstValue::Int(ConstInt { magnitude, bits, signed, negative }); |
| 4946 | } |
| 4947 | |
| 4948 | /// Apply an integer cast to a constant value, including target-width |
| 4949 | /// truncation and signed interpretation. |
| 4950 | fn castConstInt(value: ConstInt, target: Type) -> ConstValue { |
| 4951 | let raw = constIntToBits(value); |
| 4952 | let range = integerRange(target) |
| 4953 | else panic "castConstInt: expected integer type"; |
| 4954 | |
| 4955 | match range { |
| 4956 | case IntegerRange::Unsigned { bits, .. } => |
| 4957 | return ConstValue::Int(constIntFromBits(raw, bits, false)), |
| 4958 | case IntegerRange::Signed { bits, .. } => |
| 4959 | return ConstValue::Int(constIntFromBits(raw, bits, true)), |
| 4960 | } |
| 4961 | } |
| 4962 | |
| 4963 | /// Return the constant `u32` value for a slice bound when known. |
| 4964 | fn constSliceIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?u32 { |
| 4965 | let value = constValueEntry(self, node) |
| 4966 | else return nil; |
| 4967 | let case ConstValue::Int(int) = value |
| 4968 | else return nil; |
| 4969 | if int.negative { |
| 4970 | return nil; |
| 4971 | } |
| 4972 | return int.magnitude as u32; |
| 4973 | } |
| 4974 | |
| 4975 | /// Validates and extracts a non-negative integer constant from a compile-time expression. |
| 4976 | /// |
| 4977 | /// This function ensures that a node represents a valid, non-negative integer constant |
| 4978 | /// that fits within a machine word. It is used for contexts requiring compile-time |
| 4979 | /// non-negative integers, such as array sizes and alignment specifications. |
| 4980 | /// |
| 4981 | /// Returns the unsigned magnitude of the constant as `u32`. |
| 4982 | unsafe fn checkSizeInt 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> u32 |
| 4983 | throws (ResolveError) |
| 4984 | { |
| 4985 | // First traverse the node expect a numeric type. |
| 4986 | let _ = try checkNumeric(self, node); |
| 4987 | |
| 4988 | // Look up the compile-time constant value associated with this node. |
| 4989 | let value = constValueEntry(self, node) |
| 4990 | else throw emitError(self, node, ErrorKind::ConstExprRequired); |
| 4991 | |
| 4992 | let case ConstValue::Int(int) = value |
| 4993 | else panic "checkSizeInt: expected integer constant"; |
| 4994 | |
| 4995 | // Validate it fits within u32 range. |
| 4996 | if not validateConstIntRange(value, Type::U32) { |
| 4997 | throw emitError(self, node, ErrorKind::NumericLiteralOverflow); |
| 4998 | } |
| 4999 | assert not int.negative; |
| 5000 | setNodeType(self, node, Type::U32); |
| 5001 | |
| 5002 | return int.magnitude as u32; |
| 5003 | } |
| 5004 | |
| 5005 | /// Check that constructor arguments match record fields. |
| 5006 | /// |
| 5007 | /// Verifies argument count matches field count, and that each argument is |
| 5008 | /// assignable to its corresponding field type. |
| 5009 | unsafe fn checkRecordConstructorArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType) |
| 5010 | throws (ResolveError) |
| 5011 | { |
| 5012 | try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: args.len }, node); |
| 5013 | for arg, i in args { |
| 5014 | let fieldType = recInfo.fields[i].fieldType; |
| 5015 | try checkAssignable(self, arg, fieldType); |
| 5016 | } |
| 5017 | } |
| 5018 | |
| 5019 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
| 5020 | fn checkRecordArity 'arena (self: &mut Resolver 'arena, counts: CountMismatch, pattern: *ast::Node) throws (ResolveError) { |
| 5021 | if counts.actual <> counts.expected { |
| 5022 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(counts)); |
| 5023 | } |
| 5024 | } |
| 5025 | |
| 5026 | /// Helper for analyzing `constant` and `static` declarations. |
| 5027 | unsafe fn resolveConstOrStatic 'arena ( |
| 5028 | self: &mut Resolver 'arena, |
| 5029 | node: *ast::Node, |
| 5030 | ident: *ast::Node, |
| 5031 | typeNode: *ast::Node, |
| 5032 | valueNode: *ast::Node, |
| 5033 | attrList: ?ast::Attributes, |
| 5034 | isConst: bool |
| 5035 | ) -> Type throws (ResolveError) { |
| 5036 | let attrs = resolveAttributes(attrList); |
| 5037 | let bindingTy = try infer(self, typeNode); |
| 5038 | if containsRegion(bindingTy) { |
| 5039 | throw emitError(self, typeNode, ErrorKind::InvalidRefPosition); |
| 5040 | } |
| 5041 | try ensureStorableType(self, typeNode, bindingTy); |
| 5042 | let wasUnsafe = self.inUnsafeContext; |
| 5043 | set self.inUnsafeContext = wasUnsafe or ( |
| 5044 | not isConst and ast::hasAttribute(attrs, ast::Attribute::Unsafe) |
| 5045 | ); |
| 5046 | let valueTy = try checkAssignable(self, valueNode, bindingTy) catch e { |
| 5047 | set self.inUnsafeContext = wasUnsafe; |
| 5048 | throw e; |
| 5049 | }; |
| 5050 | set self.inUnsafeContext = wasUnsafe; |
| 5051 | |
| 5052 | if isConst { |
| 5053 | let mut constVal = constValueEntry(self, valueNode); |
| 5054 | if constVal == nil and not isConstExpr(self, valueNode) { |
| 5055 | throw emitError(self, valueNode, ErrorKind::ConstExprRequired); |
| 5056 | } |
| 5057 | if let val = constVal { |
| 5058 | if let case ConstValue::Int(int) = val; isNumericType(bindingTy) { |
| 5059 | set constVal = castConstInt(int, bindingTy); |
| 5060 | } |
| 5061 | } |
| 5062 | try bindConstIdent(self, ident, node, bindingTy, constVal, attrs); |
| 5063 | } else { |
| 5064 | if not isConstExpr(self, valueNode) { |
| 5065 | throw emitError(self, valueNode, ErrorKind::ConstExprRequired); |
| 5066 | } |
| 5067 | try bindValueIdent(self, ident, node, bindingTy, true, 0, attrs); |
| 5068 | } |
| 5069 | setNodeType(self, valueNode, bindingTy); |
| 5070 | |
| 5071 | return Type::Void; |
| 5072 | } |
| 5073 | |
| 5074 | /// Analyze a function declaration signature and bind the function name. |
| 5075 | unsafe fn resolveFnDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type |
| 5076 | throws (ResolveError) |
| 5077 | { |
| 5078 | let previous = self.regionScope; |
| 5079 | set self.regionScope = try bindRegions(self, node, decl.regions); |
| 5080 | let result = try resolveFnSignature(self, node, decl) catch error { |
| 5081 | set self.regionScope = previous; |
| 5082 | throw error; |
| 5083 | }; |
| 5084 | set self.regionScope = previous; |
| 5085 | return result; |
| 5086 | } |
| 5087 | |
| 5088 | /// Resolve a function signature in its declared region environment. |
| 5089 | unsafe fn resolveFnSignature 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type |
| 5090 | throws (ResolveError) |
| 5091 | { |
| 5092 | let attrMask = resolveAttributes(decl.attrs); |
| 5093 | let mut retTy = Type::Void; |
| 5094 | if let retNode = decl.sig.returnType { |
| 5095 | set retTy = try infer(self, retNode); |
| 5096 | try ensureStorableType(self, retNode, retTy); |
| 5097 | } |
| 5098 | let a = alloc::arenaAllocator(self.arena); |
| 5099 | let mut paramTypes: *mut [*Type] = &mut []; |
| 5100 | let mut throwList: *mut [*Type] = &mut []; |
| 5101 | let mut fnType = FnType { |
| 5102 | regions: self.regionScope, |
| 5103 | paramTypes: &[], |
| 5104 | returnType: allocType(self, retTy), |
| 5105 | throwList: &[], |
| 5106 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
| 5107 | }; |
| 5108 | // Enter the function scope to process parameters. |
| 5109 | enterFn(self, node, &fnType); |
| 5110 | |
| 5111 | if decl.sig.params.len > MAX_FN_PARAMS { |
| 5112 | exitFn(self); |
| 5113 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
| 5114 | expected: MAX_FN_PARAMS, |
| 5115 | actual: decl.sig.params.len, |
| 5116 | })); |
| 5117 | } |
| 5118 | for paramNode in decl.sig.params { |
| 5119 | let paramTy = try infer(self, paramNode) catch e { |
| 5120 | exitFn(self); |
| 5121 | throw e; |
| 5122 | }; |
| 5123 | paramTypes.append(allocType(self, paramTy), a); |
| 5124 | } |
| 5125 | |
| 5126 | if decl.sig.throwList.len > MAX_FN_THROWS { |
| 5127 | exitFn(self); |
| 5128 | throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch { |
| 5129 | expected: MAX_FN_THROWS, |
| 5130 | actual: decl.sig.throwList.len, |
| 5131 | })); |
| 5132 | } |
| 5133 | for throwNode in decl.sig.throwList { |
| 5134 | let throwTy = try infer(self, throwNode) catch e { |
| 5135 | exitFn(self); |
| 5136 | throw e; |
| 5137 | }; |
| 5138 | try validateErrorTag(self, throwNode, throwTy, &throwList[..]); |
| 5139 | throwList.append(allocType(self, throwTy), a); |
| 5140 | try ensureStorableType(self, throwNode, throwTy); |
| 5141 | } |
| 5142 | exitFn(self); |
| 5143 | set fnType.paramTypes = ¶mTypes[..]; |
| 5144 | set fnType.throwList = &throwList[..]; |
| 5145 | |
| 5146 | // Bind the function name. |
| 5147 | let ty = Type::Fn(allocFnType(self, fnType)); |
| 5148 | let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask) |
| 5149 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
| 5150 | |
| 5151 | return ty; |
| 5152 | } |
| 5153 | |
| 5154 | /// Analyze a function body. |
| 5155 | unsafe fn resolveFnDeclBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) { |
| 5156 | let sym = symbolFor(self, node) else { |
| 5157 | // The function declaration failed to type check, therefore |
| 5158 | // no symbol was associated with it. |
| 5159 | return; |
| 5160 | }; |
| 5161 | let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else { |
| 5162 | panic "resolveFnDeclBody: unexpected symbol data for function"; |
| 5163 | }; |
| 5164 | let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern); |
| 5165 | let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic); |
| 5166 | |
| 5167 | if let body = decl.body { |
| 5168 | if isIntrinsic { |
| 5169 | throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody); |
| 5170 | } |
| 5171 | if isExtern { |
| 5172 | throw emitError(self, node, ErrorKind::FnUnexpectedBody); |
| 5173 | } |
| 5174 | let previous = self.regionScope; |
| 5175 | set self.regionScope = try bindRegions(self, node, decl.regions); |
| 5176 | try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body) catch error { |
| 5177 | set self.regionScope = previous; |
| 5178 | throw error; |
| 5179 | }; |
| 5180 | set self.regionScope = previous; |
| 5181 | } else if not isExtern { |
| 5182 | throw emitError(self, node, ErrorKind::FnMissingBody); |
| 5183 | } |
| 5184 | } |
| 5185 | |
| 5186 | /// Resolve a function or method body and restore the enclosing context. |
| 5187 | unsafe fn resolveExecutableBody 'arena ( |
| 5188 | self: &mut Resolver 'arena, |
| 5189 | node: *ast::Node, |
| 5190 | fnType: *FnType, |
| 5191 | receiverName: ?*ast::Node, |
| 5192 | params: *[*ast::Node], |
| 5193 | body: *ast::Node, |
| 5194 | ) throws (ResolveError) { |
| 5195 | let wasUnsafe = self.inUnsafeContext; |
| 5196 | set self.inUnsafeContext = fnType.isUnsafe; |
| 5197 | // Enter function scope. |
| 5198 | enterFn(self, node, fnType); // Enter function scope for body analysis. |
| 5199 | |
| 5200 | let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e { |
| 5201 | exitFn(self); |
| 5202 | set self.inUnsafeContext = wasUnsafe; |
| 5203 | throw e; |
| 5204 | }; |
| 5205 | exitFn(self); |
| 5206 | set self.inUnsafeContext = wasUnsafe; |
| 5207 | if missingReturn { |
| 5208 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
| 5209 | } |
| 5210 | } |
| 5211 | |
| 5212 | /// Check parameters, body types, and ownership. |
| 5213 | /// Return whether a required return is missing. |
| 5214 | unsafe fn checkExecutableBody 'arena ( |
| 5215 | self: &mut Resolver 'arena, |
| 5216 | fnType: *FnType, |
| 5217 | receiverName: ?*ast::Node, |
| 5218 | params: *[*ast::Node], |
| 5219 | body: *ast::Node, |
| 5220 | ) -> bool throws (ResolveError) { |
| 5221 | if let receiver = receiverName { |
| 5222 | // Bind the receiver parameter. |
| 5223 | let receiverTy = *fnType.paramTypes[0]; |
| 5224 | try bindValueIdent(self, receiver, receiver, receiverTy, false, 0, 0); |
| 5225 | // Bind the remaining parameters from the signature. |
| 5226 | for paramNode in params { |
| 5227 | let paramTy = try infer(self, paramNode); |
| 5228 | } |
| 5229 | } |
| 5230 | // Resolve the body. |
| 5231 | let retTy = *fnType.returnType; |
| 5232 | let bodyTy = try checkAssignable(self, body, Type::Void); |
| 5233 | if retTy <> Type::Void and bodyTy <> Type::Never { |
| 5234 | return true; |
| 5235 | } |
| 5236 | // Ownership checks require complete type and call metadata. |
| 5237 | if self.errors.len == 0 { |
| 5238 | try checkLinearFn(self, receiverName, params, body); |
| 5239 | } |
| 5240 | return false; |
| 5241 | } |
| 5242 | |
| 5243 | /// Analyze a function parameter and bind its identifier. |
| 5244 | unsafe fn resolveFnParam 'arena (self: &mut Resolver 'arena, node: *ast::Node, param: ast::FnParam) -> Type |
| 5245 | throws (ResolveError) |
| 5246 | { |
| 5247 | let ty = try resolveValueType(self, param.type); |
| 5248 | let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0); |
| 5249 | |
| 5250 | return ty; |
| 5251 | } |
| 5252 | |
| 5253 | /// Compiler-known ownership markers carried by a composite declaration. |
| 5254 | record OwnershipMarkers: Copy { |
| 5255 | /// The declaration requires exact consumption. |
| 5256 | linear: bool, |
| 5257 | /// The declaration permits implicit copies. |
| 5258 | copy: bool, |
| 5259 | } |
| 5260 | |
| 5261 | /// Resolve compiler-known ownership markers from a derive list. |
| 5262 | unsafe fn resolveOwnershipMarkers 'arena (self: &mut Resolver 'arena, derives: *[*ast::Node]) -> OwnershipMarkers |
| 5263 | throws (ResolveError) |
| 5264 | { |
| 5265 | let mut result = OwnershipMarkers { linear: false, copy: false }; |
| 5266 | for derive in derives { |
| 5267 | if let case ast::NodeValue::Region { .. } = derive.value { |
| 5268 | continue; |
| 5269 | } |
| 5270 | let name = try nodeName(self, derive); |
| 5271 | if mem::eq(name, "Once") { |
| 5272 | if result.linear { |
| 5273 | throw emitError(self, derive, ErrorKind::DuplicateBinding(name)); |
| 5274 | } |
| 5275 | if result.copy { |
| 5276 | throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers); |
| 5277 | } |
| 5278 | set result.linear = true; |
| 5279 | } else if mem::eq(name, "Copy") { |
| 5280 | if result.copy { |
| 5281 | throw emitError(self, derive, ErrorKind::DuplicateBinding(name)); |
| 5282 | } |
| 5283 | if result.linear { |
| 5284 | throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers); |
| 5285 | } |
| 5286 | set result.copy = true; |
| 5287 | } else { |
| 5288 | // Resolve an ordinary trait derive. |
| 5289 | try infer(self, derive); |
| 5290 | } |
| 5291 | } |
| 5292 | return result; |
| 5293 | } |
| 5294 | |
| 5295 | /// Resolve record fields from a node list. |
| 5296 | unsafe fn resolveRecordFields 'arena (self: &mut Resolver 'arena, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType |
| 5297 | throws (ResolveError) |
| 5298 | { |
| 5299 | let a = alloc::arenaAllocator(self.arena); |
| 5300 | let mut result: *mut [RecordField] = &mut []; |
| 5301 | let mut layout = Layout { size: 0, alignment: 1 }; |
| 5302 | |
| 5303 | if fields.len > parser::MAX_RECORD_FIELDS { |
| 5304 | throw emitError(self, node, ErrorKind::Internal); |
| 5305 | } |
| 5306 | for field in fields { |
| 5307 | let case ast::NodeValue::RecordField { |
| 5308 | field: fieldNode, |
| 5309 | type: typeNode, |
| 5310 | value: valueNode |
| 5311 | } = field.value else panic "resolveRecordFields: invalid record field"; |
| 5312 | let fieldTy = try resolveValueType(self, typeNode); |
| 5313 | try ensureStorableType(self, typeNode, fieldTy); |
| 5314 | |
| 5315 | if let v = valueNode { |
| 5316 | let _valTy = try checkAssignable(self, v, fieldTy); |
| 5317 | } |
| 5318 | // Get field name for labeled records. |
| 5319 | let mut fieldName: ?*[u8] = nil; |
| 5320 | if labeled { |
| 5321 | let n = fieldNode |
| 5322 | else panic "resolveRecordFields: labeled record field missing name"; |
| 5323 | set fieldName = try nodeName(self, n); |
| 5324 | } |
| 5325 | let fieldType = typeFor(self, typeNode) |
| 5326 | else throw emitError(self, typeNode, ErrorKind::CannotInferType); |
| 5327 | |
| 5328 | // Ensure field type is fully resolved before computing layout. |
| 5329 | try ensureTypeResolved(self, fieldType, typeNode); |
| 5330 | |
| 5331 | appendRecordField(&mut result, &mut layout, fieldName, fieldType, a); |
| 5332 | } |
| 5333 | // Compute cached layout. |
| 5334 | let recordLayout = Layout { |
| 5335 | size: mem::alignUp(layout.size, layout.alignment), |
| 5336 | alignment: layout.alignment |
| 5337 | }; |
| 5338 | return RecordType { |
| 5339 | privateModule: nil, |
| 5340 | regions: nil, |
| 5341 | application: nil, |
| 5342 | fields: (&result[..]) as *unsafe [RecordField], |
| 5343 | labeled, |
| 5344 | layout: allocLayout(self, recordLayout), |
| 5345 | declaredLinear: false, |
| 5346 | declaredCopy: false, |
| 5347 | }; |
| 5348 | } |
| 5349 | |
| 5350 | /// Append an owned record field and update the layout before tail padding. |
| 5351 | fn appendRecordField(fields: &mut *mut [RecordField], layout: &mut Layout, name: ?*[u8], fieldType: Type, allocator: alloc::Allocator) { |
| 5352 | // Compute field offset by aligning to field's alignment. |
| 5353 | let fieldLayout = typeLayout(fieldType); |
| 5354 | let offset = mem::alignUp(layout.size, fieldLayout.alignment); |
| 5355 | fields.append(RecordField { name, fieldType, offset: offset as i32 }, allocator); |
| 5356 | |
| 5357 | // Advance offset past this field. |
| 5358 | set layout.size = offset + fieldLayout.size; |
| 5359 | |
| 5360 | // Track max alignment for record layout. |
| 5361 | set layout.alignment = max(layout.alignment, fieldLayout.alignment); |
| 5362 | } |
| 5363 | |
| 5364 | /// Resolve record field types for a named record declaration. |
| 5365 | unsafe fn resolveRecordBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl) |
| 5366 | throws (ResolveError) |
| 5367 | { |
| 5368 | let previous = self.regionScope; |
| 5369 | set self.regionScope = try bindRegions(self, node, decl.regions); |
| 5370 | try resolveRecordContents(self, node, decl) catch error { |
| 5371 | set self.regionScope = previous; |
| 5372 | throw error; |
| 5373 | }; |
| 5374 | set self.regionScope = previous; |
| 5375 | } |
| 5376 | |
| 5377 | /// Resolve record contents in the declaration's region environment. |
| 5378 | unsafe fn resolveRecordContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl) |
| 5379 | throws (ResolveError) |
| 5380 | { |
| 5381 | // Get the type symbol that was bound to this declaration node. |
| 5382 | // If there's no symbol, it's because an earlier phase failed. |
| 5383 | let sym = symbolFor(self, node) |
| 5384 | else return; |
| 5385 | let case SymbolData::Type(nominalTy) = sym.data |
| 5386 | else panic "resolveRecordBody: unexpected type symbol data"; |
| 5387 | |
| 5388 | // Skip if already resolved. |
| 5389 | if let case NominalType::Record(_) = *nominalTy { |
| 5390 | return; |
| 5391 | } |
| 5392 | let markers = try resolveOwnershipMarkers(self, decl.derives); |
| 5393 | set *nominalTy = NominalType::Resolving(node); |
| 5394 | let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled) catch error { |
| 5395 | set *nominalTy = NominalType::Placeholder(node); |
| 5396 | throw error; |
| 5397 | }; |
| 5398 | set recordType.regions = self.regionScope; |
| 5399 | if ast::hasAttribute(sym.attrs, ast::Attribute::Opaque) { |
| 5400 | set recordType.privateModule = self.currentMod; |
| 5401 | } |
| 5402 | if markers.copy { |
| 5403 | for field in recordType.fields { |
| 5404 | if not isCopy(field.fieldType) { |
| 5405 | throw emitError(self, node, ErrorKind::CopyContainsNonCopy); |
| 5406 | } |
| 5407 | } |
| 5408 | } |
| 5409 | set recordType.declaredLinear = markers.linear; |
| 5410 | set recordType.declaredCopy = markers.copy; |
| 5411 | |
| 5412 | set *nominalTy = NominalType::Record(recordType); |
| 5413 | } |
| 5414 | |
| 5415 | /// Bind a type name. |
| 5416 | unsafe fn bindTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
| 5417 | throws (ResolveError) |
| 5418 | { |
| 5419 | let attrMask = resolveAttributes(attrs); |
| 5420 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
| 5421 | |
| 5422 | // Create a placeholder nominal type that will be replaced in |
| 5423 | // the next phase. |
| 5424 | let nominalTy = allocNominalType(self, NominalType::Placeholder(node)); |
| 5425 | |
| 5426 | return try bindTypeIdent(self, name, node, nominalTy, attrMask); |
| 5427 | } |
| 5428 | |
| 5429 | /// Allocate a trait type descriptor and return a pointer to it. |
| 5430 | unsafe fn allocTraitType 'arena (self: &mut Resolver 'arena, name: *[u8]) -> *unsafe mut TraitType { |
| 5431 | let p = try! alloc::allocRaw(self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
| 5432 | let entry = p as *unsafe mut TraitType; |
| 5433 | set *entry = TraitType { name, moduleId: self.currentMod, methods: &mut [], supertraits: &mut [] }; |
| 5434 | |
| 5435 | return entry; |
| 5436 | } |
| 5437 | |
| 5438 | /// Bind a trait name in the current scope. |
| 5439 | unsafe fn bindTraitName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
| 5440 | throws (ResolveError) |
| 5441 | { |
| 5442 | let attrMask = resolveAttributes(attrs); |
| 5443 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
| 5444 | |
| 5445 | let traitName = try nodeName(self, name); |
| 5446 | let traitType = allocTraitType(self, traitName); |
| 5447 | let data = SymbolData::Trait(traitType); |
| 5448 | let scope = self.scope; |
| 5449 | let sym = try bindIdent(self, traitName, node, data, attrMask, scope); |
| 5450 | |
| 5451 | setNodeType(self, node, Type::Void); |
| 5452 | setNodeType(self, name, Type::Void); |
| 5453 | |
| 5454 | return sym; |
| 5455 | } |
| 5456 | |
| 5457 | /// Find a trait method by name and return its resolved metadata. |
| 5458 | export fn findTraitMethod(methods: &[TraitMethod], name: *[u8]) -> ?TraitMethod { |
| 5459 | for method in methods { |
| 5460 | if mem::eq(method.name, name) { |
| 5461 | return method; |
| 5462 | } |
| 5463 | } |
| 5464 | return nil; |
| 5465 | } |
| 5466 | |
| 5467 | /// Resolve a trait declaration body: supertrait methods, then own methods. |
| 5468 | unsafe fn resolveTraitBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node]) |
| 5469 | throws (ResolveError) |
| 5470 | { |
| 5471 | let sym = symbolFor(self, node) |
| 5472 | else return; |
| 5473 | let case SymbolData::Trait(traitType) = sym.data |
| 5474 | else return; |
| 5475 | if traitType.methods.len > 0 { |
| 5476 | return; |
| 5477 | } |
| 5478 | |
| 5479 | // Resolve supertrait bounds and copy their methods into this trait. |
| 5480 | for superNode in supertraits { |
| 5481 | let superSym = try resolveNamePath(self, superNode); |
| 5482 | let case SymbolData::Trait(superTrait) = superSym.data |
| 5483 | else throw emitError(self, superNode, ErrorKind::Internal); |
| 5484 | // Trait bodies are otherwise resolved in source order. Recursively |
| 5485 | // resolve a supertrait only when it is declared later. |
| 5486 | if superSym.node.id > node.id { |
| 5487 | let case ast::NodeValue::TraitDecl { |
| 5488 | supertraits: inheritedTraits, methods: inheritedMethods, .. |
| 5489 | } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal); |
| 5490 | try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods); |
| 5491 | } |
| 5492 | |
| 5493 | setNodeSymbol(self, superNode, superSym); |
| 5494 | |
| 5495 | let a = alloc::arenaAllocator(self.arena); |
| 5496 | if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS { |
| 5497 | throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch { |
| 5498 | expected: ast::MAX_TRAIT_METHODS, |
| 5499 | actual: traitType.methods.len as u32 + superTrait.methods.len as u32, |
| 5500 | })); |
| 5501 | } |
| 5502 | // Copy inherited methods into this trait's method table. |
| 5503 | for inherited in superTrait.methods { |
| 5504 | if let _ = findTraitMethod(&traitType.methods[..], inherited.name) { |
| 5505 | throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name)); |
| 5506 | } |
| 5507 | traitType.methods.append(TraitMethod { |
| 5508 | name: inherited.name, |
| 5509 | fnType: inherited.fnType, |
| 5510 | mutable: inherited.mutable, |
| 5511 | receiverClass: inherited.receiverClass, |
| 5512 | index: traitType.methods.len as u32, |
| 5513 | }, a); |
| 5514 | } |
| 5515 | traitType.supertraits.append(superTrait, a); |
| 5516 | } |
| 5517 | |
| 5518 | if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS { |
| 5519 | throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch { |
| 5520 | expected: ast::MAX_TRAIT_METHODS, |
| 5521 | actual: traitType.methods.len as u32 + methods.len as u32, |
| 5522 | })); |
| 5523 | } |
| 5524 | |
| 5525 | for methodNode in methods { |
| 5526 | let case ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } = methodNode.value |
| 5527 | else continue; |
| 5528 | let attrs = modifiers.attrs; |
| 5529 | let methodName = try nodeName(self, name); |
| 5530 | let attrMask = resolveAttributes(attrs); |
| 5531 | let previousRegions = self.regionScope; |
| 5532 | set self.regionScope = try bindRegions(self, methodNode, modifiers.regions); |
| 5533 | |
| 5534 | // Reject duplicate method names. |
| 5535 | if let _ = findTraitMethod(&traitType.methods[..], methodName) { |
| 5536 | throw emitError(self, name, ErrorKind::DuplicateBinding(methodName)); |
| 5537 | } |
| 5538 | // Determine the receiver class and mutability, and validate that it |
| 5539 | // points to the declaring trait. |
| 5540 | let case ast::NodeValue::TypeSig(typeSig) = receiver.value |
| 5541 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 5542 | let case ast::TypeSig::Pointer { |
| 5543 | class: receiverSyntax, valueType: receiverValueType, mutable, |
| 5544 | } = typeSig |
| 5545 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 5546 | let receiverClass = resolvePointerClass(receiverSyntax); |
| 5547 | let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value |
| 5548 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 5549 | let case ast::TypeSig::Nominal(nameNode) = innerSig |
| 5550 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 5551 | let receiverTargetName = try nodeName(self, nameNode); |
| 5552 | |
| 5553 | if receiverTargetName <> traitType.name { |
| 5554 | throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 5555 | } |
| 5556 | // Resolve parameter types and return type. |
| 5557 | let a = alloc::arenaAllocator(self.arena); |
| 5558 | let mut paramTypes: *mut [*Type] = &mut []; |
| 5559 | let mut throwList: *mut [*Type] = &mut []; |
| 5560 | let mut retType = allocType(self, Type::Void); |
| 5561 | |
| 5562 | if sig.params.len > MAX_FN_PARAMS { |
| 5563 | throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch { |
| 5564 | expected: MAX_FN_PARAMS, |
| 5565 | actual: sig.params.len, |
| 5566 | })); |
| 5567 | } |
| 5568 | for paramNode in sig.params { |
| 5569 | let case ast::NodeValue::FnParam(param) = paramNode.value |
| 5570 | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
| 5571 | let paramTy = try resolveValueType(self, param.type); |
| 5572 | paramTypes.append(allocType(self, paramTy), a); |
| 5573 | } |
| 5574 | if let ret = sig.returnType { |
| 5575 | set retType = allocType(self, try infer(self, ret)); |
| 5576 | } |
| 5577 | // Resolve throws list. |
| 5578 | if sig.throwList.len > MAX_FN_THROWS { |
| 5579 | throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch { |
| 5580 | expected: MAX_FN_THROWS, |
| 5581 | actual: sig.throwList.len, |
| 5582 | })); |
| 5583 | } |
| 5584 | for throwNode in sig.throwList { |
| 5585 | let throwTy = try infer(self, throwNode); |
| 5586 | try validateErrorTag(self, throwNode, throwTy, &throwList[..]); |
| 5587 | throwList.append(allocType(self, throwTy), a); |
| 5588 | } |
| 5589 | let fnType = FnType { |
| 5590 | regions: self.regionScope, |
| 5591 | paramTypes: ¶mTypes[..], |
| 5592 | returnType: retType, |
| 5593 | throwList: &throwList[..], |
| 5594 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
| 5595 | }; |
| 5596 | traitType.methods.append(TraitMethod { |
| 5597 | name: methodName, |
| 5598 | fnType: allocFnType(self, fnType), |
| 5599 | mutable, |
| 5600 | receiverClass, |
| 5601 | index: traitType.methods.len as u32, |
| 5602 | }, a); |
| 5603 | |
| 5604 | setNodeType(self, methodNode, Type::Void); |
| 5605 | set self.regionScope = previousRegions; |
| 5606 | } |
| 5607 | } |
| 5608 | |
| 5609 | /// Resolve a name path node to a symbol. |
| 5610 | /// Used for trait and type references in instance declarations and trait objects. |
| 5611 | unsafe fn resolveNamePath 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe mut Symbol |
| 5612 | throws (ResolveError) |
| 5613 | { |
| 5614 | match node.value { |
| 5615 | case ast::NodeValue::Ident(name) => { |
| 5616 | let sym = findAnySymbol(self.scope, name) |
| 5617 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
| 5618 | return sym; |
| 5619 | } |
| 5620 | case ast::NodeValue::ScopeAccess(access) => { |
| 5621 | let scope = self.scope; |
| 5622 | return try resolveAccess(self, node, access, scope); |
| 5623 | } |
| 5624 | else => { |
| 5625 | throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
| 5626 | } |
| 5627 | } |
| 5628 | } |
| 5629 | |
| 5630 | /// Join instance and method region parameters into one function binder. |
| 5631 | unsafe fn instanceMethodRegions 'arena ( |
| 5632 | self: &mut Resolver 'arena, instanceRegions: *[*ast::Node], methodRegions: *[*ast::Node] |
| 5633 | ) -> *[*ast::Node] { |
| 5634 | let count = instanceRegions.len + methodRegions.len; |
| 5635 | if count == 0 { |
| 5636 | return &[]; |
| 5637 | } |
| 5638 | let allocator = alloc::arenaAllocator(self.arena); |
| 5639 | let mut nodes: *mut [*ast::Node] = &mut []; |
| 5640 | for region in instanceRegions { |
| 5641 | nodes.append(region, allocator); |
| 5642 | } |
| 5643 | for region in methodRegions { |
| 5644 | nodes.append(region, allocator); |
| 5645 | } |
| 5646 | return &nodes[..]; |
| 5647 | } |
| 5648 | |
| 5649 | /// Map one region binder to a contiguous part of another binder. |
| 5650 | unsafe fn mapRegionScopes 'arena ( |
| 5651 | self: &mut Resolver 'arena, source: ?*RegionScope, target: ?*RegionScope, |
| 5652 | offset: u32, site: *ast::Node |
| 5653 | ) -> ?RegionSubstitution throws (ResolveError) { |
| 5654 | let sourceScope = source else return nil; |
| 5655 | let targetScope = target else throw emitError(self, site, ErrorKind::Internal); |
| 5656 | if offset + sourceScope.entries.len > targetScope.entries.len { |
| 5657 | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { |
| 5658 | expected: sourceScope.entries.len, |
| 5659 | actual: targetScope.entries.len - offset, |
| 5660 | })); |
| 5661 | } |
| 5662 | let map = regionSubstitution(self, sourceScope); |
| 5663 | for _, i in sourceScope.entries { |
| 5664 | set map.arguments[i] = targetScope.entries[offset + i]; |
| 5665 | } |
| 5666 | try validateRegionArguments(self, &map, nil, nil, site); |
| 5667 | return map; |
| 5668 | } |
| 5669 | |
| 5670 | /// Resolved implementation of one trait method. |
| 5671 | record ResolvedInstanceMethod: Copy { |
| 5672 | /// Canonical trait method. |
| 5673 | method: TraitMethod, |
| 5674 | /// Concrete function symbol. |
| 5675 | symbol: *unsafe mut Symbol, |
| 5676 | } |
| 5677 | |
| 5678 | /// Shared declaration state for instance method resolution. |
| 5679 | record InstanceMethodContext: Copy { |
| 5680 | /// Implemented trait. |
| 5681 | traitInfo: *unsafe TraitType, |
| 5682 | /// Instance target with declaration regions applied. |
| 5683 | concreteType: Type, |
| 5684 | /// Instance region nodes in declaration order. |
| 5685 | regions: *[*ast::Node], |
| 5686 | /// Bound instance region scope. |
| 5687 | scope: ?*RegionScope, |
| 5688 | } |
| 5689 | |
| 5690 | /// Resolve one instance method in its combined region environment. |
| 5691 | unsafe fn resolveInstanceMethod 'arena ( |
| 5692 | self: &mut Resolver 'arena, methodNode: *ast::Node, |
| 5693 | context: &InstanceMethodContext |
| 5694 | ) -> ResolvedInstanceMethod throws (ResolveError) { |
| 5695 | let case ast::NodeValue::MethodDecl { |
| 5696 | name, modifiers, receiverType, sig, .. |
| 5697 | } = methodNode.value else panic "resolveInstanceMethod: invalid method"; |
| 5698 | let combinedRegions = instanceMethodRegions(self, context.regions, modifiers.regions); |
| 5699 | let methodScope = try bindRegions(self, methodNode, combinedRegions); |
| 5700 | set self.regionScope = methodScope; |
| 5701 | |
| 5702 | let methodName = try nodeName(self, name); |
| 5703 | let attrMask = resolveAttributes(modifiers.attrs); |
| 5704 | let tm = findTraitMethod(&context.traitInfo.methods[..], methodName) |
| 5705 | else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName)); |
| 5706 | if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe { |
| 5707 | throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch); |
| 5708 | } |
| 5709 | |
| 5710 | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { |
| 5711 | class: receiverSyntax, valueType, mutable: receiverMut, |
| 5712 | }) = receiverType.value else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
| 5713 | let receiverClass = resolvePointerClass(receiverSyntax); |
| 5714 | if receiverClass <> tm.receiverClass { |
| 5715 | throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
| 5716 | } |
| 5717 | let annotatedTy = try infer(self, valueType); |
| 5718 | let mut expectedConcrete = context.concreteType; |
| 5719 | if let map = try mapRegionScopes(self, context.scope, methodScope, 0, methodNode) { |
| 5720 | set expectedConcrete = substituteRegions(self, &map, context.concreteType); |
| 5721 | } |
| 5722 | if not typesEqual(annotatedTy, expectedConcrete) { |
| 5723 | throw emitTypeMismatch(self, receiverType, TypeMismatch { expected: expectedConcrete, actual: annotatedTy }); |
| 5724 | } |
| 5725 | if tm.mutable and not receiverMut { |
| 5726 | throw emitError(self, receiverType, ErrorKind::ImmutableBinding); |
| 5727 | } |
| 5728 | if receiverMut and not tm.mutable { |
| 5729 | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
| 5730 | } |
| 5731 | |
| 5732 | let mut traitFn = tm.fnType; |
| 5733 | let mut traitRegionCount: u32 = 0; |
| 5734 | if let traitScope = tm.fnType.regions { |
| 5735 | set traitRegionCount = traitScope.entries.len; |
| 5736 | } |
| 5737 | if traitRegionCount <> modifiers.regions.len { |
| 5738 | throw emitError(self, methodNode, ErrorKind::RegionArgumentCount(CountMismatch { |
| 5739 | expected: traitRegionCount, actual: modifiers.regions.len, |
| 5740 | })); |
| 5741 | } |
| 5742 | if let map = try mapRegionScopes(self, tm.fnType.regions, methodScope, context.regions.len, methodNode) { |
| 5743 | set traitFn = substituteFnRegions(self, &map, tm.fnType, nil); |
| 5744 | } |
| 5745 | if sig.params.len <> traitFn.paramTypes.len { |
| 5746 | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 5747 | expected: traitFn.paramTypes.len, actual: sig.params.len, |
| 5748 | })); |
| 5749 | } |
| 5750 | |
| 5751 | let allocator = alloc::arenaAllocator(self.arena); |
| 5752 | let mut paramTypes: *mut [*Type] = &mut []; |
| 5753 | let receiverPtrType = Type::Pointer { |
| 5754 | class: receiverClass, target: allocType(self, annotatedTy), mutable: receiverMut, |
| 5755 | }; |
| 5756 | paramTypes.append(allocType(self, receiverPtrType), allocator); |
| 5757 | for paramNode, i in sig.params { |
| 5758 | let case ast::NodeValue::FnParam(param) = paramNode.value |
| 5759 | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
| 5760 | let instanceParamTy = try resolveValueType(self, param.type); |
| 5761 | if not typesEqual(instanceParamTy, *traitFn.paramTypes[i]) { |
| 5762 | throw emitTypeMismatch(self, paramNode, TypeMismatch { |
| 5763 | expected: *traitFn.paramTypes[i], actual: instanceParamTy, |
| 5764 | }); |
| 5765 | } |
| 5766 | paramTypes.append(allocType(self, instanceParamTy), allocator); |
| 5767 | } |
| 5768 | let mut returnType = Type::Void; |
| 5769 | if let returnNode = sig.returnType { |
| 5770 | set returnType = try resolveValueType(self, returnNode); |
| 5771 | } |
| 5772 | if not typesEqual(returnType, *traitFn.returnType) { |
| 5773 | throw emitTypeMismatch(self, methodNode, TypeMismatch { |
| 5774 | expected: *traitFn.returnType, actual: returnType, |
| 5775 | }); |
| 5776 | } |
| 5777 | if sig.throwList.len <> traitFn.throwList.len { |
| 5778 | throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch { |
| 5779 | expected: traitFn.throwList.len, actual: sig.throwList.len, |
| 5780 | })); |
| 5781 | } |
| 5782 | let mut throwList: *mut [*Type] = &mut []; |
| 5783 | for throwNode, i in sig.throwList { |
| 5784 | let throwType = try resolveValueType(self, throwNode); |
| 5785 | if not typesEqual(throwType, *traitFn.throwList[i]) { |
| 5786 | throw emitTypeMismatch(self, throwNode, TypeMismatch { |
| 5787 | expected: *traitFn.throwList[i], actual: throwType, |
| 5788 | }); |
| 5789 | } |
| 5790 | throwList.append(allocType(self, throwType), allocator); |
| 5791 | } |
| 5792 | |
| 5793 | let fnType = FnType { |
| 5794 | regions: methodScope, paramTypes: ¶mTypes[..], |
| 5795 | returnType: allocType(self, returnType), throwList: &throwList[..], |
| 5796 | isUnsafe: tm.fnType.isUnsafe, |
| 5797 | }; |
| 5798 | let fnTy = Type::Fn(allocFnType(self, fnType)); |
| 5799 | let sym = allocSymbol(self, SymbolData::Value { |
| 5800 | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
| 5801 | }, methodName, methodNode, attrMask); |
| 5802 | setNodeSymbol(self, methodNode, sym); |
| 5803 | setNodeType(self, methodNode, fnTy); |
| 5804 | setNodeType(self, name, fnTy); |
| 5805 | set self.regionScope = context.scope; |
| 5806 | return ResolvedInstanceMethod { method: tm, symbol: sym }; |
| 5807 | } |
| 5808 | |
| 5809 | /// Resolve an instance declaration. |
| 5810 | /// Validates that the trait exists, the target type exists, and all methods |
| 5811 | /// match the trait's signatures. |
| 5812 | unsafe fn resolveInstanceDecl 'arena ( |
| 5813 | self: &mut Resolver 'arena, |
| 5814 | node: *ast::Node, |
| 5815 | traitName: *ast::Node, |
| 5816 | targetType: *ast::Node, |
| 5817 | regions: *[*ast::Node], |
| 5818 | methods: *[*ast::Node] |
| 5819 | ) throws (ResolveError) { |
| 5820 | let previous = self.regionScope; |
| 5821 | set self.regionScope = try bindRegions(self, node, regions); |
| 5822 | try resolveInstanceContents(self, node, traitName, targetType, regions, methods) catch error { |
| 5823 | set self.regionScope = previous; |
| 5824 | throw error; |
| 5825 | }; |
| 5826 | set self.regionScope = previous; |
| 5827 | } |
| 5828 | |
| 5829 | /// Resolve an instance in its declaration region environment. |
| 5830 | unsafe fn resolveInstanceContents 'arena ( |
| 5831 | self: &mut Resolver 'arena, |
| 5832 | node: *ast::Node, |
| 5833 | traitName: *ast::Node, |
| 5834 | targetType: *ast::Node, |
| 5835 | regions: *[*ast::Node], |
| 5836 | methods: *[*ast::Node] |
| 5837 | ) throws (ResolveError) { |
| 5838 | let instanceScope = self.regionScope; |
| 5839 | // Look up the trait. |
| 5840 | let traitSym = try resolveNamePath(self, traitName); |
| 5841 | let case SymbolData::Trait(traitInfo) = traitSym.data |
| 5842 | else throw emitError(self, traitName, ErrorKind::Internal); |
| 5843 | |
| 5844 | setNodeSymbol(self, traitName, traitSym); |
| 5845 | |
| 5846 | // Look up the target type. |
| 5847 | let typeSym = try resolveNamePath(self, targetType); |
| 5848 | let case SymbolData::Type(nominalTy) = typeSym.data |
| 5849 | else throw emitError(self, targetType, ErrorKind::Internal); |
| 5850 | setNodeSymbol(self, targetType, typeSym); |
| 5851 | // Ensure the concrete type body is resolved. |
| 5852 | try ensureNominalResolved(self, nominalTy, targetType); |
| 5853 | |
| 5854 | // Reject duplicate instance for the same (trait, type) pair. |
| 5855 | let mut concreteInfo = nominalTy; |
| 5856 | if regions.len > 0 { |
| 5857 | set concreteInfo = try applyNominalRegions(self, nominalTy, regions, targetType); |
| 5858 | } else { |
| 5859 | try requireNominalArguments(self, nominalTy, targetType); |
| 5860 | } |
| 5861 | let concreteType = Type::Nominal(concreteInfo); |
| 5862 | if let _ = findInstance(self, traitInfo, concreteType) { |
| 5863 | throw emitError(self, node, ErrorKind::DuplicateInstance); |
| 5864 | } |
| 5865 | |
| 5866 | // Build the instance entry. |
| 5867 | if self.instancesLen >= MAX_INSTANCES { |
| 5868 | throw emitError(self, node, ErrorKind::Internal); |
| 5869 | } |
| 5870 | let methodSlice = try! alloc::allocRawSlice( |
| 5871 | self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32 |
| 5872 | ) as *unsafe mut [*unsafe mut Symbol]; |
| 5873 | let mut entry = InstanceEntry { |
| 5874 | traitType: traitInfo, |
| 5875 | concreteType, |
| 5876 | concreteTypeName: typeSym.name, |
| 5877 | moduleId: self.currentMod, |
| 5878 | methods: methodSlice, |
| 5879 | }; |
| 5880 | // Track which trait methods are covered by the instance. |
| 5881 | let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
| 5882 | let methodContext = InstanceMethodContext { |
| 5883 | traitInfo, concreteType, regions, scope: instanceScope, |
| 5884 | }; |
| 5885 | |
| 5886 | // Match each instance method to a trait method. |
| 5887 | for methodNode in methods { |
| 5888 | let resolved = try resolveInstanceMethod( |
| 5889 | self, methodNode, &methodContext |
| 5890 | ); |
| 5891 | set entry.methods[resolved.method.index] = resolved.symbol; |
| 5892 | set covered[resolved.method.index] = true; |
| 5893 | } |
| 5894 | |
| 5895 | // Fill inherited method slots from supertrait instances. |
| 5896 | for superTrait in traitInfo.supertraits { |
| 5897 | let superInst = findInstance(self, superTrait, concreteType) |
| 5898 | else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name)); |
| 5899 | for superMethod, mi in superTrait.methods { |
| 5900 | let merged = findTraitMethod(&traitInfo.methods[..], superMethod.name) |
| 5901 | else panic "resolveInstanceDecl: inherited method not found"; |
| 5902 | if not covered[merged.index] { |
| 5903 | set entry.methods[merged.index] = superInst.methods[mi]; |
| 5904 | set covered[merged.index] = true; |
| 5905 | } |
| 5906 | } |
| 5907 | } |
| 5908 | |
| 5909 | // Check that all trait methods are implemented. |
| 5910 | for method, i in traitInfo.methods { |
| 5911 | if not covered[i] { |
| 5912 | throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name)); |
| 5913 | } |
| 5914 | } |
| 5915 | set self.instances[self.instancesLen] = entry; |
| 5916 | set self.instancesLen += 1; |
| 5917 | |
| 5918 | setNodeType(self, node, Type::Void); |
| 5919 | } |
| 5920 | |
| 5921 | /// Resolve instance method bodies. |
| 5922 | unsafe fn resolveInstanceMethodBodies 'arena (self: &mut Resolver 'arena, methods: *[*ast::Node]) |
| 5923 | throws (ResolveError) |
| 5924 | { |
| 5925 | for methodNode in methods { |
| 5926 | let case ast::NodeValue::MethodDecl { .. } = methodNode.value else continue; |
| 5927 | |
| 5928 | // Symbol may be absent if [`resolveInstanceDecl`] reported an error |
| 5929 | // for this method (eg. unknown method name). Skip gracefully. |
| 5930 | if symbolFor(self, methodNode) == nil { |
| 5931 | continue; |
| 5932 | } |
| 5933 | |
| 5934 | try resolveMethodBody(self, methodNode); |
| 5935 | } |
| 5936 | } |
| 5937 | |
| 5938 | /// Resolve a method body shared by instance methods and standalone methods. |
| 5939 | /// Binds the receiver and parameters, then type-checks the body. |
| 5940 | unsafe fn resolveMethodBody 'arena ( |
| 5941 | self: &mut Resolver 'arena, |
| 5942 | node: *ast::Node, |
| 5943 | ) throws (ResolveError) { |
| 5944 | let case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } = node.value |
| 5945 | else panic "resolveMethodBody: invalid method"; |
| 5946 | let sym = symbolFor(self, node) |
| 5947 | else throw emitError(self, node, ErrorKind::Internal); |
| 5948 | let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data |
| 5949 | else panic "resolveMethodBody: expected value symbol"; |
| 5950 | let previous = self.regionScope; |
| 5951 | set self.regionScope = fnType.regions; |
| 5952 | try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body) catch error { |
| 5953 | set self.regionScope = previous; |
| 5954 | throw error; |
| 5955 | }; |
| 5956 | set self.regionScope = previous; |
| 5957 | } |
| 5958 | |
| 5959 | /// Resolve a standalone method declaration (signature only). |
| 5960 | /// Validates the receiver type and registers the method in the method table. |
| 5961 | |
| 5962 | /// Extract the type name from a resolved receiver type node. |
| 5963 | unsafe fn receiverTypeName 'arena ( |
| 5964 | self: &mut Resolver 'arena, |
| 5965 | receiverType: *ast::Node, |
| 5966 | ) -> *[u8] throws (ResolveError) { |
| 5967 | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = |
| 5968 | receiverType.value |
| 5969 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
| 5970 | let mut nameNode: *ast::Node = valueType; |
| 5971 | match valueType.value { |
| 5972 | case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(name)) => set nameNode = name, |
| 5973 | case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name, .. }) => set nameNode = name, |
| 5974 | else => throw emitError(self, receiverType, ErrorKind::Internal), |
| 5975 | } |
| 5976 | let sym = symbolFor(self, nameNode) |
| 5977 | else throw emitError(self, receiverType, ErrorKind::Internal); |
| 5978 | |
| 5979 | return sym.name; |
| 5980 | } |
| 5981 | |
| 5982 | /// Resolve and register a standalone method declaration. |
| 5983 | unsafe fn resolveMethodDecl 'arena ( |
| 5984 | self: &mut Resolver 'arena, |
| 5985 | node: *ast::Node, |
| 5986 | ) throws (ResolveError) { |
| 5987 | let case ast::NodeValue::MethodDecl { modifiers, .. } = node.value |
| 5988 | else panic "resolveMethodDecl: invalid method"; |
| 5989 | let previous = self.regionScope; |
| 5990 | set self.regionScope = try bindRegions(self, node, modifiers.regions); |
| 5991 | try resolveMethodSignature(self, node) catch error { |
| 5992 | set self.regionScope = previous; |
| 5993 | throw error; |
| 5994 | }; |
| 5995 | set self.regionScope = previous; |
| 5996 | } |
| 5997 | |
| 5998 | /// Resolve a standalone method signature in its region environment. |
| 5999 | unsafe fn resolveMethodSignature 'arena ( |
| 6000 | self: &mut Resolver 'arena, |
| 6001 | node: *ast::Node, |
| 6002 | ) throws (ResolveError) { |
| 6003 | let case ast::NodeValue::MethodDecl { |
| 6004 | name, modifiers, receiverType, sig, .. |
| 6005 | } = node.value else panic "resolveMethodSignature: invalid method"; |
| 6006 | // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a |
| 6007 | // nominal type. |
| 6008 | let fullReceiverTy = try infer(self, receiverType); |
| 6009 | let case Type::Pointer { |
| 6010 | class: receiverClass, target: receiverTarget, mutable: receiverMut, |
| 6011 | } = fullReceiverTy |
| 6012 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
| 6013 | let concreteType = *receiverTarget; |
| 6014 | let case Type::Nominal(nominalTy) = concreteType |
| 6015 | else throw emitError(self, receiverType, ErrorKind::ExpectedRecord); |
| 6016 | try ensureNominalResolved(self, nominalTy, receiverType); |
| 6017 | |
| 6018 | // Get the type name from the inner type node's symbol. |
| 6019 | let typeName = try receiverTypeName(self, receiverType); |
| 6020 | let methodName = try nodeName(self, name); |
| 6021 | let attrMask = resolveAttributes(modifiers.attrs); |
| 6022 | |
| 6023 | // Reject duplicate method for the same (type, name). |
| 6024 | if let _ = findMethod(self, concreteType, methodName) { |
| 6025 | throw emitError(self, name, ErrorKind::DuplicateBinding(methodName)); |
| 6026 | } |
| 6027 | |
| 6028 | // Resolve parameter types. |
| 6029 | let a = alloc::arenaAllocator(self.arena); |
| 6030 | let mut paramTypes: *mut [*Type] = &mut []; |
| 6031 | |
| 6032 | // Receiver is the first parameter. |
| 6033 | let receiverPtrType = Type::Pointer { |
| 6034 | class: receiverClass, |
| 6035 | target: allocType(self, concreteType), |
| 6036 | mutable: receiverMut, |
| 6037 | }; |
| 6038 | paramTypes.append(allocType(self, receiverPtrType), a); |
| 6039 | |
| 6040 | for paramNode in sig.params { |
| 6041 | let case ast::NodeValue::FnParam(param) = paramNode.value |
| 6042 | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
| 6043 | let paramTy = try resolveValueType(self, param.type); |
| 6044 | paramTypes.append(allocType(self, paramTy), a); |
| 6045 | } |
| 6046 | |
| 6047 | // Resolve return type. |
| 6048 | let mut returnType = Type::Void; |
| 6049 | if let retNode = sig.returnType { |
| 6050 | set returnType = try resolveValueType(self, retNode); |
| 6051 | } |
| 6052 | |
| 6053 | // Resolve throw list. |
| 6054 | let mut throwTypes: *mut [*Type] = &mut []; |
| 6055 | for throwNode in sig.throwList { |
| 6056 | let throwTy = try resolveValueType(self, throwNode); |
| 6057 | try validateErrorTag(self, throwNode, throwTy, &throwTypes[..]); |
| 6058 | throwTypes.append(allocType(self, throwTy), a); |
| 6059 | } |
| 6060 | |
| 6061 | let retTypePtr = allocType(self, returnType); |
| 6062 | let throwList = &throwTypes[..]; |
| 6063 | |
| 6064 | let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe); |
| 6065 | // Full function type (receiver + params) for lowering. |
| 6066 | let fullFnType = FnType { |
| 6067 | regions: self.regionScope, |
| 6068 | paramTypes: ¶mTypes[..], |
| 6069 | returnType: retTypePtr, |
| 6070 | throwList, |
| 6071 | isUnsafe, |
| 6072 | }; |
| 6073 | let fullFnInfo = allocFnType(self, fullFnType); |
| 6074 | let fnTy = Type::Fn(fullFnInfo); |
| 6075 | |
| 6076 | // Function type excluding receiver, for call arg checking. |
| 6077 | let checkFnType = FnType { |
| 6078 | regions: self.regionScope, |
| 6079 | paramTypes: ¶mTypes[1..], |
| 6080 | returnType: retTypePtr, |
| 6081 | throwList, |
| 6082 | isUnsafe, |
| 6083 | }; |
| 6084 | |
| 6085 | // Create a symbol for the method without binding it into the module scope. |
| 6086 | let sym = allocSymbol(self, SymbolData::Value { |
| 6087 | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
| 6088 | }, methodName, node, attrMask); |
| 6089 | |
| 6090 | setNodeSymbol(self, node, sym); |
| 6091 | setNodeType(self, node, fnTy); |
| 6092 | setNodeType(self, name, fnTy); |
| 6093 | |
| 6094 | // Register in the method table. |
| 6095 | if self.methodsLen >= MAX_METHODS { |
| 6096 | throw emitError(self, node, ErrorKind::Internal); |
| 6097 | } |
| 6098 | set self.methods[self.methodsLen] = MethodEntry { |
| 6099 | moduleId: self.currentMod, |
| 6100 | concreteType, |
| 6101 | concreteTypeName: typeName, |
| 6102 | name: methodName, |
| 6103 | fnType: allocFnType(self, checkFnType), |
| 6104 | mutable: receiverMut, |
| 6105 | receiverClass, |
| 6106 | symbolId: sym.id, |
| 6107 | fullFnType: fullFnInfo, |
| 6108 | }; |
| 6109 | set self.methodsLen += 1; |
| 6110 | } |
| 6111 | |
| 6112 | /// Look up an instance entry by trait and concrete type. |
| 6113 | unsafe fn findInstance 'arena (self: &Resolver 'arena, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
| 6114 | for i in 0..self.instancesLen { |
| 6115 | let entry: *unsafe InstanceEntry = &self.instances[i]; |
| 6116 | if entry.traitType == traitInfo and erasedTypesEqual(entry.concreteType, concreteType) { |
| 6117 | return entry; |
| 6118 | } |
| 6119 | } |
| 6120 | return nil; |
| 6121 | } |
| 6122 | |
| 6123 | /// Look up a standalone method by concrete type and name. |
| 6124 | export unsafe fn findMethod 'arena (self: &Resolver 'arena, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry { |
| 6125 | for i in 0..self.methodsLen { |
| 6126 | let entry: *unsafe MethodEntry = &self.methods[i]; |
| 6127 | if erasedTypesEqual(entry.concreteType, concreteType) and entry.name == name { |
| 6128 | return entry; |
| 6129 | } |
| 6130 | } |
| 6131 | return nil; |
| 6132 | } |
| 6133 | |
| 6134 | /// Look up standalone method metadata by its resolver-local symbol identity. |
| 6135 | export fn findMethodBySymbol 'arena (self: &Resolver 'arena, symbolId: u32) -> ?MethodEntry { |
| 6136 | for i in 0..self.methodsLen { |
| 6137 | let entry = self.methods[i]; |
| 6138 | if entry.symbolId == symbolId { |
| 6139 | return entry; |
| 6140 | } |
| 6141 | } |
| 6142 | return nil; |
| 6143 | } |
| 6144 | |
| 6145 | /// Resolve union variant types after all type names are bound (Phase 2 of type resolution). |
| 6146 | unsafe fn resolveUnionBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl) |
| 6147 | throws (ResolveError) |
| 6148 | { |
| 6149 | let previous = self.regionScope; |
| 6150 | set self.regionScope = try bindRegions(self, node, decl.regions); |
| 6151 | try resolveUnionContents(self, node, decl) catch error { |
| 6152 | set self.regionScope = previous; |
| 6153 | throw error; |
| 6154 | }; |
| 6155 | set self.regionScope = previous; |
| 6156 | } |
| 6157 | |
| 6158 | /// Resolve union contents in the declaration's region environment. |
| 6159 | unsafe fn resolveUnionContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl) |
| 6160 | throws (ResolveError) |
| 6161 | { |
| 6162 | // Get the type symbol that was bound to this declaration node. |
| 6163 | // If there's no symbol, it's because an earlier phase failed. |
| 6164 | let sym = symbolFor(self, node) |
| 6165 | else return; |
| 6166 | let case SymbolData::Type(nominalTy) = sym.data |
| 6167 | else panic "resolveUnionBody: unexpected symbol data"; |
| 6168 | |
| 6169 | // Check if already resolved, in which case there's no need to |
| 6170 | // do it again. |
| 6171 | if let case NominalType::Union(_) = *nominalTy { |
| 6172 | return; |
| 6173 | } |
| 6174 | let a = alloc::arenaAllocator(self.arena); |
| 6175 | let mut variants: *mut [UnionVariant] = &mut []; |
| 6176 | |
| 6177 | let markers = try resolveOwnershipMarkers(self, decl.derives); |
| 6178 | set *nominalTy = NominalType::Resolving(node); |
| 6179 | |
| 6180 | assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded"; |
| 6181 | let mut iota: u32 = 0; |
| 6182 | for variantNode, i in decl.variants { |
| 6183 | let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value |
| 6184 | else panic "resolveUnionBody: invalid union variant"; |
| 6185 | let variantName = try nodeName(self, variantDecl.name); |
| 6186 | // Resolve the variant's payload type if present. |
| 6187 | let mut variantType = Type::Void; |
| 6188 | if let typeNode = variantDecl.type { |
| 6189 | set variantType = try infer(self, typeNode); |
| 6190 | try ensureStorableType(self, typeNode, variantType); |
| 6191 | try ensureTypeResolved(self, variantType, typeNode); |
| 6192 | } |
| 6193 | // Process the variant's explicit discriminant value if present. |
| 6194 | try visitOptional(self, variantDecl.value, variantType); |
| 6195 | let tag = variantTag(variantDecl, &mut iota); |
| 6196 | // Create a symbol for this variant. |
| 6197 | let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag }; |
| 6198 | let variantSym = allocSymbol(self, data, variantName, variantNode, 0); |
| 6199 | |
| 6200 | variants.append(UnionVariant { |
| 6201 | name: variantName, |
| 6202 | valueType: variantType, |
| 6203 | symbol: variantSym, |
| 6204 | }, a); |
| 6205 | } |
| 6206 | if markers.copy { |
| 6207 | for variant in &variants[..] { |
| 6208 | if not isCopy(variant.valueType) { |
| 6209 | throw emitError(self, node, ErrorKind::CopyContainsNonCopy); |
| 6210 | } |
| 6211 | } |
| 6212 | } |
| 6213 | let info = computeUnionLayout(&variants[..]); |
| 6214 | |
| 6215 | // Update the nominal type with the resolved variants. |
| 6216 | set *nominalTy = NominalType::Union(UnionType { |
| 6217 | regions: self.regionScope, |
| 6218 | application: nil, |
| 6219 | variants: (&variants[..]) as *unsafe [UnionVariant], |
| 6220 | layout: allocLayout(self, info.layout), |
| 6221 | valOffset: info.valOffset, |
| 6222 | isAllVoid: info.isAllVoid, |
| 6223 | declaredLinear: markers.linear, |
| 6224 | declaredCopy: markers.copy, |
| 6225 | }); |
| 6226 | } |
| 6227 | |
| 6228 | /// Check whether an attributed module or import is active in this build. |
| 6229 | /// A test module is active only when its source module was registered. |
| 6230 | fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool { |
| 6231 | if let attributes = attrs { |
| 6232 | if ast::attributesContains(&attributes, ast::Attribute::Test) { |
| 6233 | if not self.config.buildTest { |
| 6234 | return false; |
| 6235 | } |
| 6236 | if let moduleName = name { |
| 6237 | return findChildModule(self, moduleName, self.currentMod) <> nil; |
| 6238 | } |
| 6239 | } |
| 6240 | } |
| 6241 | return true; |
| 6242 | } |
| 6243 | |
| 6244 | /// Analyze a module during the graph analysis phase. |
| 6245 | unsafe fn resolveModGraph 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod) |
| 6246 | throws (ResolveError) |
| 6247 | { |
| 6248 | let modName = try nodeName(self, decl.name); |
| 6249 | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
| 6250 | return; |
| 6251 | } |
| 6252 | let attrMask = resolveAttributes(decl.attrs); |
| 6253 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
| 6254 | let submod = try enterSubModule(self, modName, node); |
| 6255 | |
| 6256 | // Bind the module symbol in the outer scope, ie. where the `mod` statement is. |
| 6257 | try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope); |
| 6258 | let case ast::NodeValue::Block(block) = submod.root.value |
| 6259 | else panic "resolveModGraph: expected block for module root"; |
| 6260 | try resolveModuleGraph(self, &block); |
| 6261 | |
| 6262 | exitModuleScope(self, submod); |
| 6263 | } |
| 6264 | |
| 6265 | /// Analyze a module in the declaration phase. |
| 6266 | unsafe fn resolveModDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod) |
| 6267 | throws (ResolveError) |
| 6268 | { |
| 6269 | // Find module under the current module. |
| 6270 | let modName = try nodeName(self, decl.name); |
| 6271 | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
| 6272 | return; |
| 6273 | } |
| 6274 | let submod = try enterSubModule(self, modName, node); |
| 6275 | let case ast::NodeValue::Block(block) = submod.root.value |
| 6276 | else panic "resolveModDecl: expected block for module root"; |
| 6277 | try resolveModuleDecls(self, &block); |
| 6278 | |
| 6279 | exitModuleScope(self, submod); |
| 6280 | } |
| 6281 | |
| 6282 | /// Analyze a `use` statement and create a symbol for the imported module. |
| 6283 | unsafe fn resolveUse 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Use) -> Type |
| 6284 | throws (ResolveError) |
| 6285 | { |
| 6286 | if not shouldAnalyzeModule(self, decl.attrs, nil) { |
| 6287 | return Type::Void; |
| 6288 | } |
| 6289 | let resolved = try resolveModulePath(self, decl.path); |
| 6290 | let attrMask = resolveAttributes(decl.attrs); |
| 6291 | |
| 6292 | if decl.wildcard { |
| 6293 | // Import all public symbols from the target module. |
| 6294 | for i in 0..resolved.scope.symbolsLen { |
| 6295 | let sym = resolved.scope.symbols[i]; |
| 6296 | if ast::hasAttribute(sym.attrs, ast::Attribute::Export) { |
| 6297 | if let existing = findSymbolInScope(self.scope, sym.name) { |
| 6298 | if existing == sym { |
| 6299 | continue; |
| 6300 | } |
| 6301 | } |
| 6302 | let scope = self.scope; |
| 6303 | try addSymbolToScope(self, sym, scope, node); |
| 6304 | } |
| 6305 | } |
| 6306 | } else { |
| 6307 | // Regular module import. |
| 6308 | let scope = self.scope; |
| 6309 | try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope); |
| 6310 | } |
| 6311 | return Type::Void; |
| 6312 | } |
| 6313 | |
| 6314 | /// Analyze a standard `if` statement. |
| 6315 | unsafe fn resolveIf 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::If) -> Type |
| 6316 | throws (ResolveError) |
| 6317 | { |
| 6318 | try checkBoolean(self, cond.condition); |
| 6319 | let thenTy = try visit(self, cond.thenBranch, Type::Void); |
| 6320 | let elseTy = try visitOptional(self, cond.elseBranch, Type::Void); |
| 6321 | |
| 6322 | return setNodeType(self, node, unifyBranches(thenTy, elseTy)); |
| 6323 | } |
| 6324 | |
| 6325 | /// Analyze a conditional expression. |
| 6326 | unsafe fn resolveCondExpr 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::CondExpr, hint: Type) -> Type |
| 6327 | throws (ResolveError) |
| 6328 | { |
| 6329 | try checkBoolean(self, cond.condition); |
| 6330 | let thenValue = try visit(self, cond.thenExpr, hint); |
| 6331 | let thenTy = assignableValueType(self, cond.thenExpr, thenValue); |
| 6332 | let elseValue = try visit(self, cond.elseExpr, hint); |
| 6333 | let elseTy = assignableValueType(self, cond.elseExpr, elseValue); |
| 6334 | |
| 6335 | // Either branch may supply the concrete type for an otherwise context- |
| 6336 | // dependent expression, such as an unsuffixed integer or `nil`. |
| 6337 | if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) { |
| 6338 | setNodeCoercion(self, cond.elseExpr, coercion); |
| 6339 | return setNodeType(self, node, thenTy); |
| 6340 | } |
| 6341 | if let coercion = isAssignable(self, elseTy, thenTy, cond.thenExpr) { |
| 6342 | setNodeCoercion(self, cond.thenExpr, coercion); |
| 6343 | return setNodeType(self, node, elseTy); |
| 6344 | } |
| 6345 | try expectAssignable(self, thenTy, elseTy, cond.elseExpr); |
| 6346 | |
| 6347 | return setNodeType(self, node, thenTy); |
| 6348 | } |
| 6349 | |
| 6350 | /// Analyze a pattern match structure (used by if-let, while-let). |
| 6351 | unsafe fn resolvePatternMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, pat: &ast::PatternMatch) |
| 6352 | throws (ResolveError) |
| 6353 | { |
| 6354 | match pat.kind { |
| 6355 | case ast::PatternKind::Case => { |
| 6356 | // Analyze pattern against scrutinee type. |
| 6357 | let scrutineeTy = try infer(self, pat.scrutinee); |
| 6358 | if isUnsafePointerType(scrutineeTy) { |
| 6359 | try requireUnsafe(self, pat.scrutinee); |
| 6360 | } |
| 6361 | let subject = unwrapMatchSubject(scrutineeTy); |
| 6362 | try resolveCasePattern(self, pat.pattern, subject.effectiveTy, IdentMode::Compare, subject.by); |
| 6363 | } |
| 6364 | case ast::PatternKind::Binding => { |
| 6365 | // Scrutinee must be optional, bind the payload. |
| 6366 | let scrutineeTy = try checkOptional(self, pat.scrutinee); |
| 6367 | let payloadTy = *scrutineeTy; |
| 6368 | |
| 6369 | try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0); |
| 6370 | setNodeType(self, pat.pattern, payloadTy); |
| 6371 | } |
| 6372 | } |
| 6373 | if let guard = pat.guard { |
| 6374 | try checkBoolean(self, guard); |
| 6375 | } |
| 6376 | } |
| 6377 | |
| 6378 | /// Analyze an `if let` or `if let case` pattern binding. |
| 6379 | unsafe fn resolveIfLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::IfLet) -> Type |
| 6380 | throws (ResolveError) |
| 6381 | { |
| 6382 | enterScope(self, node); |
| 6383 | try resolvePatternMatch(self, node, &cond.pattern); |
| 6384 | |
| 6385 | let thenTy = try visit(self, cond.thenBranch, Type::Void); |
| 6386 | exitScope(self); |
| 6387 | |
| 6388 | let elseTy = try visitOptional(self, cond.elseBranch, Type::Void); |
| 6389 | |
| 6390 | return setNodeType(self, node, unifyBranches(thenTy, elseTy)); |
| 6391 | } |
| 6392 | |
| 6393 | /// Controls how bare identifiers are handled in case patterns. |
| 6394 | union IdentMode: Copy { |
| 6395 | /// Identifier is a value to compare against. |
| 6396 | Compare, |
| 6397 | /// Identifier introduces a new binding. |
| 6398 | Bind, |
| 6399 | } |
| 6400 | |
| 6401 | /// Check whether a pattern node is a destructuring pattern that looks |
| 6402 | /// through structure (union variant, record literal, scope access). |
| 6403 | /// Identifiers, placeholders, and plain literals are not destructuring. |
| 6404 | export fn isDestructuringPattern(pattern: *ast::Node) -> bool { |
| 6405 | match pattern.value { |
| 6406 | case ast::NodeValue::Call(_), |
| 6407 | ast::NodeValue::RecordLit(_), |
| 6408 | ast::NodeValue::ScopeAccess(_) => return true, |
| 6409 | else => return false, |
| 6410 | } |
| 6411 | } |
| 6412 | |
| 6413 | /// Analyze a case pattern for match, if-case, let-case, or while-case. |
| 6414 | /// |
| 6415 | /// At the top level, bare identifiers are compared against existing values. |
| 6416 | /// Inside destructuring patterns (arrays, records), identifiers become bindings. |
| 6417 | unsafe fn resolveCasePattern 'arena ( |
| 6418 | self: &mut Resolver 'arena, |
| 6419 | pattern: *ast::Node, |
| 6420 | scrutineeTy: Type, |
| 6421 | mode: IdentMode, |
| 6422 | matchBy: MatchBy |
| 6423 | ) throws (ResolveError) { |
| 6424 | if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) { |
| 6425 | if isUnsafePointerType(scrutineeTy) { |
| 6426 | try requireUnsafe(self, pattern); |
| 6427 | } |
| 6428 | try resolveCasePattern(self, pattern, *target, mode, matchBy); |
| 6429 | return; |
| 6430 | } |
| 6431 | // TODO: Collapse these nested matches. |
| 6432 | match scrutineeTy { |
| 6433 | case Type::Nominal(info) => { |
| 6434 | try ensureNominalResolved(self, info, pattern); |
| 6435 | |
| 6436 | match *info { |
| 6437 | case NominalType::Union(unionType) => { |
| 6438 | try resolveUnionPattern(self, pattern, scrutineeTy, unionType, matchBy); |
| 6439 | return; |
| 6440 | } |
| 6441 | case NominalType::Record(recInfo) => { |
| 6442 | match pattern.value { |
| 6443 | case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => { |
| 6444 | try resolveRecordPattern(self, pattern, scrutineeTy, recInfo, matchBy); |
| 6445 | return; |
| 6446 | } else => {} |
| 6447 | } |
| 6448 | } else => {} |
| 6449 | } |
| 6450 | } |
| 6451 | case Type::Array(arrayInfo) => { |
| 6452 | if let case ast::NodeValue::ArrayLit(items) = pattern.value { |
| 6453 | if items.len as u32 <> arrayInfo.length { |
| 6454 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch( |
| 6455 | CountMismatch { expected: arrayInfo.length, actual: items.len as u32 } |
| 6456 | )); |
| 6457 | } |
| 6458 | let elemTy = *arrayInfo.item; |
| 6459 | for item in items { |
| 6460 | try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy); |
| 6461 | } |
| 6462 | setNodeType(self, pattern, scrutineeTy); |
| 6463 | return; |
| 6464 | } |
| 6465 | } else => {} |
| 6466 | } |
| 6467 | // Handle non-binding patterns (literals, placeholders) and bindings. |
| 6468 | match pattern.value { |
| 6469 | case ast::NodeValue::Placeholder => { |
| 6470 | // Placeholder matches without introducing bindings. |
| 6471 | } |
| 6472 | case ast::NodeValue::Ident(_) => { |
| 6473 | match mode { |
| 6474 | case IdentMode::Bind => try bindPatternVar(self, pattern, scrutineeTy, matchBy), |
| 6475 | case IdentMode::Compare => try checkAssignable(self, pattern, scrutineeTy), |
| 6476 | } |
| 6477 | } |
| 6478 | else => { |
| 6479 | // Literals and other expressions: check type compatibility. |
| 6480 | try checkAssignable(self, pattern, scrutineeTy); |
| 6481 | } |
| 6482 | } |
| 6483 | } |
| 6484 | |
| 6485 | /// Analyze a traditional `while` loop. |
| 6486 | unsafe fn resolveWhile 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::While) -> Type |
| 6487 | throws (ResolveError) |
| 6488 | { |
| 6489 | try checkBoolean(self, loopNode.condition); |
| 6490 | let loopTy = try visitLoop(self, loopNode.body); |
| 6491 | try visitOptional(self, loopNode.elseBranch, Type::Void); |
| 6492 | |
| 6493 | if loopNode.condition.value == ast::NodeValue::Bool(true) { |
| 6494 | return setNodeType(self, node, loopTy); |
| 6495 | } |
| 6496 | return setNodeType(self, node, Type::Void); |
| 6497 | } |
| 6498 | |
| 6499 | /// Analyze a `while let` loop with pattern binding. |
| 6500 | unsafe fn resolveWhileLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::WhileLet) -> Type |
| 6501 | throws (ResolveError) |
| 6502 | { |
| 6503 | enterScope(self, node); |
| 6504 | try resolvePatternMatch(self, node, &loopNode.pattern); |
| 6505 | |
| 6506 | try visitLoop(self, loopNode.body); |
| 6507 | exitScope(self); |
| 6508 | |
| 6509 | try visitOptional(self, loopNode.elseBranch, Type::Void); |
| 6510 | |
| 6511 | return setNodeType(self, node, Type::Void); |
| 6512 | } |
| 6513 | |
| 6514 | /// Store complete iteration metadata and return the loop binding type. |
| 6515 | fn resolveForInfo 'arena ( |
| 6516 | self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For, iterableTy: Type |
| 6517 | ) -> Type throws (ResolveError) { |
| 6518 | // Extract binding names for the lowerer. |
| 6519 | let mut bindingName: ?*[u8] = nil; |
| 6520 | if let case ast::NodeValue::Ident(name) = forStmt.binding.value { |
| 6521 | set bindingName = name; |
| 6522 | } |
| 6523 | let mut indexName: ?*[u8] = nil; |
| 6524 | if let idx = forStmt.index { |
| 6525 | if let case ast::NodeValue::Ident(name) = idx.value { |
| 6526 | set indexName = name; |
| 6527 | } |
| 6528 | } |
| 6529 | // Extract item type and store pre-computed loop metadata for the lowerer. |
| 6530 | match iterableTy { |
| 6531 | case Type::Slice { item, class, .. } => { |
| 6532 | if class == types::PointerClass::Unsafe { |
| 6533 | try requireUnsafe(self, forStmt.iterable); |
| 6534 | } |
| 6535 | setForLoopInfo(self, node, ForLoopInfo::Collection { |
| 6536 | elemType: item, length: nil, bindingName, indexName |
| 6537 | }); |
| 6538 | return *item; |
| 6539 | } |
| 6540 | case Type::Range { start, .. } => { |
| 6541 | // Iterable ranges must have a start, and since we enforce type |
| 6542 | // equality for start and end, that is always the item type. |
| 6543 | let valType = start else { |
| 6544 | throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable); |
| 6545 | }; |
| 6546 | let case ast::NodeValue::Range(range) = forStmt.iterable.value else { |
| 6547 | throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable); |
| 6548 | }; |
| 6549 | setForLoopInfo(self, node, ForLoopInfo::Range { |
| 6550 | valType, range, bindingName, indexName |
| 6551 | }); |
| 6552 | return *valType; |
| 6553 | } |
| 6554 | case Type::Array(arrayInfo) => { |
| 6555 | setForLoopInfo(self, node, ForLoopInfo::Collection { |
| 6556 | elemType: arrayInfo.item, |
| 6557 | length: arrayInfo.length, |
| 6558 | bindingName, |
| 6559 | indexName, |
| 6560 | }); |
| 6561 | return *arrayInfo.item; |
| 6562 | } |
| 6563 | else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable), |
| 6564 | } |
| 6565 | } |
| 6566 | |
| 6567 | /// Analyze a `for` loop, binding iteration variables. |
| 6568 | unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type |
| 6569 | throws (ResolveError) |
| 6570 | { |
| 6571 | let iterableTy = try infer(self, forStmt.iterable); |
| 6572 | let itemTy = try resolveForInfo(self, node, forStmt, iterableTy); |
| 6573 | enterScope(self, node); |
| 6574 | try bindForLoopPattern(self, forStmt.binding, itemTy, false); |
| 6575 | |
| 6576 | if let pat = forStmt.index { |
| 6577 | try bindForLoopPattern(self, pat, Type::U32, false); |
| 6578 | } |
| 6579 | // The lowerer always creates at least one internal variable for iteration, |
| 6580 | // even when the binding is a placeholder or no explicit index is given. |
| 6581 | if let owner = self.currentFnNode { |
| 6582 | set self.nodeData.entries[owner.id].localCount += 1; |
| 6583 | } |
| 6584 | try visitLoop(self, forStmt.body); |
| 6585 | exitScope(self); |
| 6586 | |
| 6587 | try visitOptional(self, forStmt.elseBranch, Type::Void); |
| 6588 | |
| 6589 | return setNodeType(self, node, Type::Void); |
| 6590 | } |
| 6591 | |
| 6592 | /// Get the node within a pattern that carries the `UnionVariant` extra. |
| 6593 | /// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the |
| 6594 | /// type name, and for `Call` it is the callee. |
| 6595 | export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node { |
| 6596 | match pattern.value { |
| 6597 | case ast::NodeValue::ScopeAccess(_) => return pattern, |
| 6598 | case ast::NodeValue::RecordLit(lit) => return lit.typeName, |
| 6599 | case ast::NodeValue::Call(call) => return call.callee, |
| 6600 | else => return nil, |
| 6601 | } |
| 6602 | } |
| 6603 | |
| 6604 | /// Get the i-th sub-pattern element from a compound pattern. |
| 6605 | /// For `RecordLit` this is the i-th field's value; for `Call` it is the |
| 6606 | /// i-th argument. |
| 6607 | fn patternSubElement(pattern: *ast::Node, idx: u32) -> ?*ast::Node { |
| 6608 | match pattern.value { |
| 6609 | case ast::NodeValue::RecordLit(lit) => { |
| 6610 | if idx < lit.fields.len as u32 { |
| 6611 | if let case ast::NodeValue::RecordLitField(field) = lit.fields[idx].value { |
| 6612 | return field.value; |
| 6613 | } |
| 6614 | } |
| 6615 | } |
| 6616 | case ast::NodeValue::Call(call) => { |
| 6617 | if idx < call.args.len as u32 { |
| 6618 | return call.args[idx]; |
| 6619 | } |
| 6620 | } |
| 6621 | else => {} |
| 6622 | } |
| 6623 | return nil; |
| 6624 | } |
| 6625 | |
| 6626 | /// Get the number of sub-pattern elements in a compound pattern. |
| 6627 | fn patternSubCount(pattern: *ast::Node) -> u32 { |
| 6628 | match pattern.value { |
| 6629 | case ast::NodeValue::RecordLit(lit) => return lit.fields.len as u32, |
| 6630 | case ast::NodeValue::Call(call) => return call.args.len as u32, |
| 6631 | else => return 0, |
| 6632 | } |
| 6633 | } |
| 6634 | |
| 6635 | /// Check whether a pattern contains nested sub-patterns that further |
| 6636 | /// refine the match beyond the outer variant (e.g. nested union variant |
| 6637 | /// tests or literal comparisons). Used to allow the same outer variant |
| 6638 | /// to appear in multiple match arms. |
| 6639 | fn hasNestedRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool { |
| 6640 | for i in 0..patternSubCount(pattern) { |
| 6641 | if let sub = patternSubElement(pattern, i) { |
| 6642 | if isRefiningPattern(self, sub) { |
| 6643 | return true; |
| 6644 | } |
| 6645 | } |
| 6646 | } |
| 6647 | return false; |
| 6648 | } |
| 6649 | |
| 6650 | /// Check whether a single pattern node is a refining pattern that tests |
| 6651 | /// a value rather than just binding it. Union variants, literals, and |
| 6652 | /// scope accesses are refining; identifiers, placeholders, and plain |
| 6653 | /// record destructurings are not. |
| 6654 | fn isRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool { |
| 6655 | match pattern.value { |
| 6656 | case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => |
| 6657 | return false, |
| 6658 | case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => { |
| 6659 | if let keyNode = patternVariantKeyNode(pattern) { |
| 6660 | if let case NodeExtra::UnionVariant { .. } = self.nodeData.entries[keyNode.id].extra { |
| 6661 | return true; |
| 6662 | } |
| 6663 | } |
| 6664 | // Plain record destructuring / non-variant call is not directly |
| 6665 | // refining; recurse to check sub-patterns. |
| 6666 | return hasNestedRefiningPattern(self, pattern); |
| 6667 | } |
| 6668 | case ast::NodeValue::ArrayLit(items) => { |
| 6669 | for item in items { |
| 6670 | if isRefiningPattern(self, item) { |
| 6671 | return true; |
| 6672 | } |
| 6673 | } |
| 6674 | return false; |
| 6675 | } |
| 6676 | case ast::NodeValue::ScopeAccess(_) => |
| 6677 | return true, |
| 6678 | else => |
| 6679 | return true, |
| 6680 | } |
| 6681 | } |
| 6682 | |
| 6683 | /// Check whether any pattern in a case prong matches unconditionally. |
| 6684 | /// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies. |
| 6685 | /// Note: top-level identifiers in `case` are comparisons, not bindings, |
| 6686 | /// so they do not count as wildcards. |
| 6687 | fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool { |
| 6688 | for pattern in patterns { |
| 6689 | match pattern.value { |
| 6690 | case ast::NodeValue::Placeholder => return true, |
| 6691 | case ast::NodeValue::ArrayLit(items) => { |
| 6692 | if isIrrefutableArrayPattern(items) { |
| 6693 | return true; |
| 6694 | } |
| 6695 | } |
| 6696 | else => {} |
| 6697 | } |
| 6698 | } |
| 6699 | return false; |
| 6700 | } |
| 6701 | |
| 6702 | /// Check whether all elements of an array pattern are irrefutable. |
| 6703 | /// Inside array patterns, identifiers are bindings, not comparisons. |
| 6704 | fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool { |
| 6705 | for item in items { |
| 6706 | match item.value { |
| 6707 | case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {} |
| 6708 | case ast::NodeValue::ArrayLit(inner) => { |
| 6709 | if not isIrrefutableArrayPattern(inner) { |
| 6710 | return false; |
| 6711 | } |
| 6712 | } |
| 6713 | else => return false, |
| 6714 | } |
| 6715 | } |
| 6716 | return true; |
| 6717 | } |
| 6718 | |
| 6719 | /// Classify a match prong and reject unreachable prongs after a catch-all. |
| 6720 | /// Record whether lowering can omit the prong's pattern test. |
| 6721 | fn checkMatchProng 'arena ( |
| 6722 | self: &mut Resolver 'arena, |
| 6723 | prongNode: *ast::Node, |
| 6724 | prong: ast::MatchProng, |
| 6725 | subjectTy: Type, |
| 6726 | state: &mut MatchState |
| 6727 | ) throws (ResolveError) { |
| 6728 | // Whether this prong is catch-all. |
| 6729 | let mut isCatchAll = false; |
| 6730 | |
| 6731 | if prong.guard <> nil { |
| 6732 | set state.isConst = false; |
| 6733 | } else { |
| 6734 | match prong.arm { |
| 6735 | case ast::ProngArm::Binding(_) => { |
| 6736 | // For optionals, a binding matches only a present value. |
| 6737 | set isCatchAll = not isOptionalType(subjectTy); |
| 6738 | }, |
| 6739 | case ast::ProngArm::Else => set isCatchAll = true, |
| 6740 | case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns), |
| 6741 | } |
| 6742 | } |
| 6743 | if state.catchAll { |
| 6744 | if isCatchAll { |
| 6745 | throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll); |
| 6746 | } |
| 6747 | throw emitError(self, prongNode, ErrorKind::CatchAllMustBeLast); |
| 6748 | } |
| 6749 | if isCatchAll { |
| 6750 | set state.catchAll = true; |
| 6751 | } |
| 6752 | setProngCatchAll(self, prongNode, isCatchAll); |
| 6753 | |
| 6754 | } |
| 6755 | |
| 6756 | /// Analyze a `match` expression. Dispatches to specialized functions based on |
| 6757 | /// the subject type. |
| 6758 | unsafe fn resolveMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match) -> Type |
| 6759 | throws (ResolveError) |
| 6760 | { |
| 6761 | let subjectTy = try infer(self, sw.subject); |
| 6762 | if isUnsafePointerType(subjectTy) { |
| 6763 | try requireUnsafe(self, sw.subject); |
| 6764 | } |
| 6765 | let subject = unwrapMatchSubject(subjectTy); |
| 6766 | |
| 6767 | if let case Type::Optional(inner) = subject.effectiveTy { |
| 6768 | try resolveMatchOptional(self, node, sw, inner, subject.by); |
| 6769 | } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy { |
| 6770 | try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by); |
| 6771 | } else { |
| 6772 | try resolveMatchGeneric(self, node, sw, subject.effectiveTy, subject.by); |
| 6773 | } |
| 6774 | |
| 6775 | // Mark last non-guarded prong as exhaustive. |
| 6776 | let lastProng = sw.prongs[sw.prongs.len - 1]; |
| 6777 | let case ast::NodeValue::MatchProng(p) = lastProng.value |
| 6778 | else panic "resolveMatch: expected match prong"; |
| 6779 | if p.guard == nil { |
| 6780 | setProngCatchAll(self, lastProng, true); |
| 6781 | } |
| 6782 | let ty = typeFor(self, node) else { |
| 6783 | return Type::Void; |
| 6784 | }; |
| 6785 | return ty; |
| 6786 | } |
| 6787 | |
| 6788 | /// Analyze a `match` expression on an optional subject. |
| 6789 | unsafe fn resolveMatchOptional 'arena ( |
| 6790 | self: &mut Resolver 'arena, |
| 6791 | node: *ast::Node, |
| 6792 | sw: ast::Match, |
| 6793 | innerTy: *Type, |
| 6794 | matchBy: MatchBy |
| 6795 | ) -> Type throws (ResolveError) |
| 6796 | { |
| 6797 | let subjectTy = Type::Optional(innerTy); |
| 6798 | let prongs = sw.prongs; |
| 6799 | let mut hasValue = false; |
| 6800 | let mut hasNil = false; |
| 6801 | let mut state = MatchState { catchAll: false, isConst: false }; |
| 6802 | let mut matchType = Type::Never; |
| 6803 | |
| 6804 | for prongNode in prongs { |
| 6805 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
| 6806 | else panic "resolveMatchOptional: expected match prong"; |
| 6807 | |
| 6808 | try checkMatchProng(self, prongNode, prong, subjectTy, &mut state); |
| 6809 | set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
| 6810 | |
| 6811 | // Track coverage. Guarded prongs don't count as covering a case. |
| 6812 | if prong.guard == nil { |
| 6813 | if let case ast::ProngArm::Binding(_) = prong.arm { |
| 6814 | if hasValue { |
| 6815 | throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern); |
| 6816 | } |
| 6817 | set hasValue = true; |
| 6818 | } else if let case ast::ProngArm::Case(patterns) = prong.arm { |
| 6819 | for pat in patterns { |
| 6820 | if let case ast::NodeValue::Nil = pat.value { |
| 6821 | if hasNil { |
| 6822 | throw emitError(self, pat, ErrorKind::DuplicateMatchPattern); |
| 6823 | } |
| 6824 | set hasNil = true; |
| 6825 | } |
| 6826 | } |
| 6827 | } |
| 6828 | } |
| 6829 | } |
| 6830 | |
| 6831 | // Check exhaustiveness. |
| 6832 | if not state.catchAll { |
| 6833 | if not hasValue { |
| 6834 | throw emitError(self, node, ErrorKind::OptionalMatchMissingValue); |
| 6835 | } |
| 6836 | if not hasNil { |
| 6837 | throw emitError(self, node, ErrorKind::OptionalMatchMissingNil); |
| 6838 | } |
| 6839 | } else if hasValue and hasNil { |
| 6840 | throw emitError(self, node, ErrorKind::UnreachableElse); |
| 6841 | } |
| 6842 | return setNodeType(self, node, matchType); |
| 6843 | } |
| 6844 | |
| 6845 | /// Analyze a `match` expression on a union subject. |
| 6846 | unsafe fn resolveMatchUnion 'arena ( |
| 6847 | self: &mut Resolver 'arena, |
| 6848 | node: *ast::Node, |
| 6849 | sw: ast::Match, |
| 6850 | subjectTy: Type, |
| 6851 | info: UnionType, |
| 6852 | matchBy: MatchBy |
| 6853 | ) -> Type throws (ResolveError) { |
| 6854 | let prongs = sw.prongs; |
| 6855 | let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS]; |
| 6856 | let mut coveredCount: u32 = 0; |
| 6857 | let mut state = MatchState { catchAll: false, isConst: false }; |
| 6858 | let mut matchType = Type::Never; |
| 6859 | |
| 6860 | for prongNode in prongs { |
| 6861 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
| 6862 | else panic "resolveMatchUnion: expected match prong"; |
| 6863 | |
| 6864 | try checkMatchProng(self, prongNode, prong, subjectTy, &mut state); |
| 6865 | set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
| 6866 | |
| 6867 | // Guarded prongs don't count as covering. Patterns with nested |
| 6868 | // refining sub-patterns (e.g. matching different inner union variants) |
| 6869 | // don't count as duplicates or as fully covering. |
| 6870 | if prong.guard == nil { |
| 6871 | if let case ast::ProngArm::Case(patterns) = prong.arm { |
| 6872 | for pattern in patterns { |
| 6873 | if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra { |
| 6874 | if not hasNestedRefiningPattern(self, pattern) { |
| 6875 | if covered[ix] { |
| 6876 | throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern); |
| 6877 | } |
| 6878 | set covered[ix] = true; |
| 6879 | set coveredCount += 1; |
| 6880 | } |
| 6881 | } |
| 6882 | } |
| 6883 | } |
| 6884 | } |
| 6885 | } |
| 6886 | // Check that all variants are covered. |
| 6887 | if not state.catchAll { |
| 6888 | for variant, i in info.variants { |
| 6889 | if not covered[i] { |
| 6890 | throw emitError( |
| 6891 | self, node, ErrorKind::UnionMatchNonExhaustive(variant.name) |
| 6892 | ); |
| 6893 | } |
| 6894 | } |
| 6895 | } else if coveredCount == info.variants.len as u32 { |
| 6896 | throw emitError(self, node, ErrorKind::UnreachableElse); |
| 6897 | } |
| 6898 | return setNodeType(self, node, matchType); |
| 6899 | } |
| 6900 | |
| 6901 | /// Analyze a `match` expression on a generic subject type. Requires exhaustiveness: |
| 6902 | /// booleans must cover both `true` and `false`, other types require a catch-all. |
| 6903 | unsafe fn resolveMatchGeneric 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match, subjectTy: Type, matchBy: MatchBy) -> Type |
| 6904 | throws (ResolveError) |
| 6905 | { |
| 6906 | let prongs = sw.prongs; |
| 6907 | let mut state = MatchState { catchAll: false, isConst: true }; |
| 6908 | let mut matchType = Type::Never; |
| 6909 | let mut hasTrue = false; |
| 6910 | let mut hasFalse = false; |
| 6911 | let mut hasConstCase = false; |
| 6912 | |
| 6913 | for prongNode in prongs { |
| 6914 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
| 6915 | else panic "resolveMatchGeneric: expected match prong"; |
| 6916 | |
| 6917 | try checkMatchProng(self, prongNode, prong, subjectTy, &mut state); |
| 6918 | set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
| 6919 | // Track boolean coverage. Guarded prongs don't count as covering. |
| 6920 | if let case ast::ProngArm::Case(patterns) = prong.arm { |
| 6921 | for p in patterns { |
| 6922 | if prong.guard == nil { |
| 6923 | if let case ast::NodeValue::Bool(val) = p.value { |
| 6924 | if (val and hasTrue) or (not val and hasFalse) { |
| 6925 | throw emitError(self, p, ErrorKind::DuplicateMatchPattern); |
| 6926 | } |
| 6927 | if val { |
| 6928 | set hasTrue = true; |
| 6929 | } else { |
| 6930 | set hasFalse = true; |
| 6931 | } |
| 6932 | } |
| 6933 | } |
| 6934 | // Scalar constant patterns allow the match to be lowered |
| 6935 | // to a switch instruction. |
| 6936 | if let c = constValueEntry(self, p) { |
| 6937 | match c { |
| 6938 | case ConstValue::Bool(_), ConstValue::Char(_), ConstValue::Int(_) => |
| 6939 | set hasConstCase = true, |
| 6940 | else => |
| 6941 | set state.isConst = false, |
| 6942 | } |
| 6943 | } |
| 6944 | } |
| 6945 | } |
| 6946 | } |
| 6947 | |
| 6948 | // Check exhaustiveness. |
| 6949 | if not state.catchAll { |
| 6950 | if let case Type::Bool = subjectTy { |
| 6951 | if not hasTrue { |
| 6952 | throw emitError(self, node, ErrorKind::BoolMatchMissing(true)); |
| 6953 | } |
| 6954 | if not hasFalse { |
| 6955 | throw emitError(self, node, ErrorKind::BoolMatchMissing(false)); |
| 6956 | } |
| 6957 | } else { |
| 6958 | throw emitError(self, node, ErrorKind::MatchNonExhaustive); |
| 6959 | } |
| 6960 | } else if let case Type::Bool = subjectTy { |
| 6961 | if hasTrue and hasFalse { |
| 6962 | throw emitError(self, node, ErrorKind::UnreachableElse); |
| 6963 | } |
| 6964 | } |
| 6965 | setMatchConst(self, node, state.isConst and hasConstCase); |
| 6966 | |
| 6967 | return setNodeType(self, node, matchType); |
| 6968 | } |
| 6969 | |
| 6970 | /// Analyze a single `match` prong branch. Returns the unified match type. |
| 6971 | unsafe fn visitMatchProng 'arena ( |
| 6972 | self: &mut Resolver 'arena, |
| 6973 | node: *ast::Node, |
| 6974 | prongNode: ast::MatchProng, |
| 6975 | subjectTy: Type, |
| 6976 | matchType: Type, |
| 6977 | matchBy: MatchBy |
| 6978 | ) -> Type throws (ResolveError) { |
| 6979 | enterScope(self, node); |
| 6980 | let prongTy = try resolveMatchProngBody(self, prongNode, subjectTy, matchBy) catch e { |
| 6981 | exitScope(self); |
| 6982 | throw e; |
| 6983 | }; |
| 6984 | exitScope(self); |
| 6985 | setNodeType(self, node, prongTy); |
| 6986 | |
| 6987 | return unifyBranches(matchType, prongTy); |
| 6988 | } |
| 6989 | |
| 6990 | /// Analyze the contents of a `match` prong while inside the prong scope. |
| 6991 | unsafe fn resolveMatchProngBody 'arena ( |
| 6992 | self: &mut Resolver 'arena, |
| 6993 | prong: ast::MatchProng, |
| 6994 | subjectTy: Type, |
| 6995 | matchBy: MatchBy |
| 6996 | ) -> Type throws (ResolveError) { |
| 6997 | match prong.arm { |
| 6998 | case ast::ProngArm::Binding(pat) => { |
| 6999 | // For optionals, bind the unwrapped inner type. |
| 7000 | let mut bindTy = subjectTy; |
| 7001 | if let case Type::Optional(inner) = subjectTy { |
| 7002 | set bindTy = *inner; |
| 7003 | } |
| 7004 | try bindPatternVar(self, pat, bindTy, matchBy); |
| 7005 | } |
| 7006 | case ast::ProngArm::Case(patterns) => { |
| 7007 | for pattern in patterns { |
| 7008 | try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy); |
| 7009 | } |
| 7010 | } |
| 7011 | case ast::ProngArm::Else => {} |
| 7012 | } |
| 7013 | if let g = prong.guard { |
| 7014 | try checkBoolean(self, g); |
| 7015 | } |
| 7016 | return try visit(self, prong.body, Type::Void); |
| 7017 | } |
| 7018 | |
| 7019 | /// Ensure a scope access pattern references a compatible union variant. |
| 7020 | unsafe fn resolveUnionScopePattern 'arena ( |
| 7021 | self: &mut Resolver 'arena, |
| 7022 | pattern: *ast::Node, |
| 7023 | access: ast::Access, |
| 7024 | subjectTy: Type, |
| 7025 | unionType: UnionType |
| 7026 | ) throws (ResolveError) { |
| 7027 | let patternTy = try visit(self, pattern, subjectTy); |
| 7028 | if not isComparable(patternTy, subjectTy) { |
| 7029 | throw emitTypeMismatch(self, pattern, TypeMismatch { |
| 7030 | expected: subjectTy, |
| 7031 | actual: patternTy, |
| 7032 | }); |
| 7033 | } |
| 7034 | let case NodeExtra::UnionVariant { ordinal: index, .. } = self.nodeData.entries[pattern.id].extra else { |
| 7035 | throw emitError(self, pattern, ErrorKind::Internal); |
| 7036 | }; |
| 7037 | let variant = &unionType.variants[index]; |
| 7038 | // If this variant has a payload, throw an error, since the user hasn't |
| 7039 | // provided one. |
| 7040 | if variant.valueType <> Type::Void { |
| 7041 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name)); |
| 7042 | } |
| 7043 | } |
| 7044 | |
| 7045 | /// Validate and bind a union constructor call used as a `match` pattern. |
| 7046 | unsafe fn resolveUnionCallPattern 'arena ( |
| 7047 | self: &mut Resolver 'arena, |
| 7048 | pattern: *ast::Node, |
| 7049 | call: ast::Call, |
| 7050 | subjectTy: Type, |
| 7051 | unionType: UnionType, |
| 7052 | matchBy: MatchBy |
| 7053 | ) throws (ResolveError) { |
| 7054 | let calleeTy = try checkEqual(self, call.callee, subjectTy); |
| 7055 | let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else { |
| 7056 | throw emitError(self, call.callee, ErrorKind::Internal); |
| 7057 | }; |
| 7058 | let variant = &unionType.variants[index]; |
| 7059 | // Copy variant index to the pattern node for the lowerer. |
| 7060 | setVariantInfo(self, pattern, index, tag); |
| 7061 | |
| 7062 | if variant.valueType <> Type::Void { |
| 7063 | try bindUnionPatternPayload(self, pattern, call, variant.name, variant.valueType, matchBy); |
| 7064 | } else { |
| 7065 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
| 7066 | } |
| 7067 | } |
| 7068 | |
| 7069 | /// Bind the payload introduced by a union constructor pattern. |
| 7070 | unsafe fn bindUnionPatternPayload 'arena ( |
| 7071 | self: &mut Resolver 'arena, |
| 7072 | pattern: *ast::Node, |
| 7073 | call: ast::Call, |
| 7074 | variantName: *[u8], |
| 7075 | payloadTy: Type, |
| 7076 | matchBy: MatchBy |
| 7077 | ) throws (ResolveError) { |
| 7078 | if call.args.len == 0 { |
| 7079 | throw emitError( |
| 7080 | self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName) |
| 7081 | ); |
| 7082 | } |
| 7083 | // All variant payloads are records. |
| 7084 | try ensureTypeResolved(self, payloadTy, pattern); |
| 7085 | let recInfo = getRecord(payloadTy) |
| 7086 | else panic "bindUnionPatternPayload: payload is not a record"; |
| 7087 | |
| 7088 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
| 7089 | } |
| 7090 | |
| 7091 | /// Bind a pattern variable. For ref matches, wraps the type in a pointer. |
| 7092 | unsafe fn bindPatternVar 'arena (self: &mut Resolver 'arena, binding: *ast::Node, ty: Type, matchBy: MatchBy) |
| 7093 | throws (ResolveError) |
| 7094 | { |
| 7095 | let mut bindTy = ty; |
| 7096 | match matchBy { |
| 7097 | case MatchBy::Value => {} |
| 7098 | case MatchBy::Ref(class) => set bindTy = Type::Pointer { |
| 7099 | class, |
| 7100 | target: allocType(self, ty), |
| 7101 | mutable: false, |
| 7102 | }, |
| 7103 | case MatchBy::MutRef => set bindTy = Type::Pointer { |
| 7104 | class: types::PointerClass::Ref, |
| 7105 | target: allocType(self, ty), |
| 7106 | mutable: true, |
| 7107 | }, |
| 7108 | } |
| 7109 | match binding.value { |
| 7110 | case ast::NodeValue::Placeholder => { |
| 7111 | // Nothing to do. |
| 7112 | } |
| 7113 | case ast::NodeValue::Ident(_) => { |
| 7114 | try bindValueIdent(self, binding, binding, bindTy, false, 0, 0); |
| 7115 | } |
| 7116 | else => { |
| 7117 | // Nested pattern: recursively resolve (record destructuring, |
| 7118 | // union variant, scope access, call, literals, etc). |
| 7119 | try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy); |
| 7120 | } |
| 7121 | } |
| 7122 | } |
| 7123 | |
| 7124 | /// Check a record pattern's exact nominal type before binding its fields. |
| 7125 | unsafe fn resolveRecordPattern 'arena ( |
| 7126 | self: &mut Resolver 'arena, pattern: *ast::Node, subjectTy: Type, body: RecordType, matchBy: MatchBy |
| 7127 | ) throws (ResolveError) { |
| 7128 | let mut name: ?*ast::Node = nil; |
| 7129 | match pattern.value { |
| 7130 | case ast::NodeValue::Call(call) => set name = call.callee, |
| 7131 | case ast::NodeValue::RecordLit(lit) => set name = lit.typeName, |
| 7132 | else => panic "resolveRecordPattern: expected record pattern", |
| 7133 | } |
| 7134 | if let typeName = name { |
| 7135 | let actual = try visit(self, typeName, subjectTy); |
| 7136 | let symbol = symbolFor(self, typeName) else throw emitError(self, typeName, ErrorKind::ExpectedRecord); |
| 7137 | let case SymbolData::Type(_) = symbol.data else throw emitError(self, typeName, ErrorKind::ExpectedRecord); |
| 7138 | if not typesEqual(actual, subjectTy) { |
| 7139 | throw emitTypeMismatch(self, typeName, TypeMismatch { expected: subjectTy, actual }); |
| 7140 | } |
| 7141 | } |
| 7142 | setNodeType(self, pattern, subjectTy); |
| 7143 | try bindRecordPatternFields(self, pattern, body, matchBy); |
| 7144 | } |
| 7145 | |
| 7146 | /// Require access to a record's module-owned representation. |
| 7147 | fn requireRecordAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, recordType: RecordType) |
| 7148 | throws (ResolveError) |
| 7149 | { |
| 7150 | if let owner = recordType.privateModule; owner <> self.currentMod { |
| 7151 | throw emitError(self, node, ErrorKind::OpaqueRecordAccess); |
| 7152 | } |
| 7153 | } |
| 7154 | |
| 7155 | /// Bind record pattern fields to variables in the current scope. |
| 7156 | unsafe fn bindRecordPatternFields 'arena ( |
| 7157 | self: &mut Resolver 'arena, |
| 7158 | pattern: *ast::Node, |
| 7159 | recInfo: RecordType, |
| 7160 | matchBy: MatchBy |
| 7161 | ) throws (ResolveError) { |
| 7162 | try requireRecordAccess(self, pattern, recInfo); |
| 7163 | match pattern.value { |
| 7164 | case ast::NodeValue::Call(call) => { |
| 7165 | // Unlabeled patterns: `S(x, y)`. |
| 7166 | try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: call.args.len }, pattern); |
| 7167 | |
| 7168 | for binding, i in call.args { |
| 7169 | let fieldType = recInfo.fields[i].fieldType; |
| 7170 | try bindPatternVar(self, binding, fieldType, matchBy); |
| 7171 | } |
| 7172 | } |
| 7173 | case ast::NodeValue::RecordLit(lit) => { |
| 7174 | // Labeled patterns: `T { x, y }` or `T { x: binding }`. |
| 7175 | if not lit.ignoreRest { |
| 7176 | try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: lit.fields.len }, pattern); |
| 7177 | } |
| 7178 | for fieldNode in lit.fields { |
| 7179 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value |
| 7180 | else panic "expected RecordLitField"; |
| 7181 | |
| 7182 | // Brace patterns require labeled fields. |
| 7183 | let label = field.label else panic "expected labeled field"; |
| 7184 | let fieldName = try nodeName(self, label); |
| 7185 | let fieldIndex = findRecordField(&recInfo.fields[..], fieldName) |
| 7186 | else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName)); |
| 7187 | let fieldType = recInfo.fields[fieldIndex].fieldType; |
| 7188 | // Store field index for the lowerer. |
| 7189 | setRecordFieldIndex(self, fieldNode, fieldIndex); |
| 7190 | try bindPatternVar(self, field.value, fieldType, matchBy); |
| 7191 | } |
| 7192 | } |
| 7193 | else => throw emitError(self, pattern, ErrorKind::Internal) |
| 7194 | } |
| 7195 | } |
| 7196 | |
| 7197 | /// Validate and bind a record literal pattern for matching labeled union variants. |
| 7198 | unsafe fn resolveUnionRecordPattern 'arena ( |
| 7199 | self: &mut Resolver 'arena, |
| 7200 | pattern: *ast::Node, |
| 7201 | lit: ast::RecordLit, |
| 7202 | subjectTy: Type, |
| 7203 | unionType: UnionType, |
| 7204 | matchBy: MatchBy |
| 7205 | ) throws (ResolveError) { |
| 7206 | let typeName = lit.typeName else { |
| 7207 | throw emitError(self, pattern, ErrorKind::Internal); |
| 7208 | }; |
| 7209 | // Verify the type matches the subject. |
| 7210 | let patternTy = try visit(self, typeName, subjectTy); |
| 7211 | if not isComparable(patternTy, subjectTy) { |
| 7212 | throw emitTypeMismatch(self, pattern, TypeMismatch { |
| 7213 | expected: subjectTy, |
| 7214 | actual: patternTy, |
| 7215 | }); |
| 7216 | } |
| 7217 | let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[typeName.id].extra else { |
| 7218 | throw emitError(self, typeName, ErrorKind::Internal); |
| 7219 | }; |
| 7220 | let variant = &unionType.variants[index]; |
| 7221 | |
| 7222 | // Copy variant index to the pattern node for the lowerer. |
| 7223 | setVariantInfo(self, pattern, index, tag); |
| 7224 | |
| 7225 | if variant.valueType == Type::Void { |
| 7226 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
| 7227 | } |
| 7228 | try ensureTypeResolved(self, variant.valueType, pattern); |
| 7229 | let recInfo = getRecord(variant.valueType) |
| 7230 | else panic "resolveUnionRecordPattern: payload is not a record"; |
| 7231 | |
| 7232 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
| 7233 | } |
| 7234 | |
| 7235 | /// Analyze a pattern appearing in a union case. |
| 7236 | unsafe fn resolveUnionPattern 'arena ( |
| 7237 | self: &mut Resolver 'arena, |
| 7238 | pattern: *ast::Node, |
| 7239 | subjectTy: Type, |
| 7240 | unionType: UnionType, |
| 7241 | matchBy: MatchBy |
| 7242 | ) throws (ResolveError) { |
| 7243 | match pattern.value { |
| 7244 | case ast::NodeValue::ScopeAccess(access) => |
| 7245 | try resolveUnionScopePattern(self, pattern, access, subjectTy, unionType), |
| 7246 | case ast::NodeValue::Call(call) => |
| 7247 | try resolveUnionCallPattern(self, pattern, call, subjectTy, unionType, matchBy), |
| 7248 | case ast::NodeValue::RecordLit(lit) => |
| 7249 | try resolveUnionRecordPattern(self, pattern, lit, subjectTy, unionType, matchBy), |
| 7250 | else => { |
| 7251 | let patternTy = try visit(self, pattern, subjectTy); |
| 7252 | throw emitTypeMismatch(self, pattern, TypeMismatch { |
| 7253 | expected: subjectTy, |
| 7254 | actual: patternTy, |
| 7255 | }); |
| 7256 | } |
| 7257 | } |
| 7258 | } |
| 7259 | |
| 7260 | /// Return whether a case pattern introduces value bindings. |
| 7261 | fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool { |
| 7262 | match pattern.value { |
| 7263 | case ast::NodeValue::Ident(_) => return nested, |
| 7264 | case ast::NodeValue::Call(call) => { |
| 7265 | for arg in call.args { |
| 7266 | if casePatternIntroducesBindings(arg, true) { |
| 7267 | return true; |
| 7268 | } |
| 7269 | } |
| 7270 | } |
| 7271 | case ast::NodeValue::RecordLit(lit) => { |
| 7272 | for fieldNode in lit.fields { |
| 7273 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value |
| 7274 | else continue; |
| 7275 | if casePatternIntroducesBindings(field.value, true) { |
| 7276 | return true; |
| 7277 | } |
| 7278 | } |
| 7279 | } |
| 7280 | case ast::NodeValue::ArrayLit(items) => { |
| 7281 | for item in items { |
| 7282 | if casePatternIntroducesBindings(item, true) { |
| 7283 | return true; |
| 7284 | } |
| 7285 | } |
| 7286 | } |
| 7287 | else => {} |
| 7288 | } |
| 7289 | return false; |
| 7290 | } |
| 7291 | |
| 7292 | /// Analyze a `let-else` guard. |
| 7293 | unsafe fn resolveLetElse 'arena (self: &mut Resolver 'arena, node: *ast::Node, letElse: ast::LetElse) -> Type |
| 7294 | throws (ResolveError) |
| 7295 | { |
| 7296 | let pat = letElse.pattern; |
| 7297 | let exprTy = try infer(self, pat.scrutinee); |
| 7298 | |
| 7299 | match pat.kind { |
| 7300 | case ast::PatternKind::Binding => { |
| 7301 | // Simple binding requires an optional expression. |
| 7302 | let case Type::Optional(inner) = exprTy else { |
| 7303 | throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional); |
| 7304 | }; |
| 7305 | let payloadTy = *inner; |
| 7306 | // The `else` branch supplies the binding when the optional is nil. |
| 7307 | try checkAssignable(self, letElse.elseBranch, payloadTy); |
| 7308 | let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0); |
| 7309 | |
| 7310 | return setNodeType(self, node, Type::Void); |
| 7311 | } |
| 7312 | case ast::PatternKind::Case => { |
| 7313 | // Resolve the failure path before introducing success-only bindings. |
| 7314 | let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy); |
| 7315 | try resolveCasePattern( |
| 7316 | self, |
| 7317 | pat.pattern, |
| 7318 | exprTy, |
| 7319 | IdentMode::Compare, |
| 7320 | MatchBy::Value, |
| 7321 | ); |
| 7322 | if let guardExpr = pat.guard { |
| 7323 | try checkBoolean(self, guardExpr); |
| 7324 | } |
| 7325 | if elseTy <> Type::Never and |
| 7326 | casePatternIntroducesBindings(pat.pattern, false) |
| 7327 | { |
| 7328 | throw emitError( |
| 7329 | self, |
| 7330 | letElse.elseBranch, |
| 7331 | ErrorKind::LinearLetElseMustTerminate, |
| 7332 | ); |
| 7333 | } |
| 7334 | } |
| 7335 | } |
| 7336 | return setNodeType(self, node, Type::Void); |
| 7337 | } |
| 7338 | |
| 7339 | /// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`. |
| 7340 | unsafe fn resolveBuiltinCall 'arena ( |
| 7341 | self: &mut Resolver 'arena, |
| 7342 | node: *ast::Node, |
| 7343 | kind: ast::Builtin, |
| 7344 | args: *[*ast::Node] |
| 7345 | ) -> Type throws (ResolveError) { |
| 7346 | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
| 7347 | if kind == ast::Builtin::SliceOf { |
| 7348 | if args.len <> 2 and args.len <> 3 { |
| 7349 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
| 7350 | expected: 2, |
| 7351 | actual: args.len as u32, |
| 7352 | })); |
| 7353 | } |
| 7354 | let ptrType = try visit(self, args[0], Type::Unknown); |
| 7355 | let case Type::Pointer { class, target, mutable } = ptrType else { |
| 7356 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
| 7357 | }; |
| 7358 | let _ = try checkAssignable(self, args[1], Type::U32); |
| 7359 | if args.len == 3 { |
| 7360 | let _ = try checkAssignable(self, args[2], Type::U32); |
| 7361 | } |
| 7362 | try requireUnsafe(self, node); |
| 7363 | return setNodeType(self, node, Type::Slice { class, item: target, mutable }); |
| 7364 | } |
| 7365 | if args.len <> 1 { |
| 7366 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
| 7367 | expected: 1, |
| 7368 | actual: args.len as u32, |
| 7369 | })); |
| 7370 | } |
| 7371 | |
| 7372 | let ty = try resolveValueType(self, args[0]); |
| 7373 | // Ensure the type body is resolved before computing layout. |
| 7374 | // TODO: Somehow, ensuring the type is resolved should just happen all |
| 7375 | // the time, lazily. |
| 7376 | try ensureTypeResolved(self, ty, args[0]); |
| 7377 | // TODO: This should be stored in `symbol` instead of having to recompute it. |
| 7378 | // That way there's a canonical place to look for code gen. |
| 7379 | let layout = getTypeLayout(ty); |
| 7380 | |
| 7381 | // Evaluate the built-in. |
| 7382 | let mut value: u32 = undefined; |
| 7383 | match kind { |
| 7384 | case ast::Builtin::SizeOf => { |
| 7385 | set value = layout.size; |
| 7386 | }, |
| 7387 | case ast::Builtin::AlignOf => { |
| 7388 | set value = layout.alignment; |
| 7389 | }, |
| 7390 | case ast::Builtin::SliceOf => { |
| 7391 | panic "unreachable: @sliceOf handled above"; |
| 7392 | } |
| 7393 | } |
| 7394 | // Record as constant value for constant folding. |
| 7395 | setNodeConstValue(self, node, ConstValue::Int(ConstInt { |
| 7396 | magnitude: value as u64, |
| 7397 | bits: 32, |
| 7398 | signed: false, |
| 7399 | negative: false, |
| 7400 | })); |
| 7401 | return setNodeType(self, node, Type::U32); |
| 7402 | } |
| 7403 | |
| 7404 | /// Allocate an initially empty argument map for a region-parameterized signature. |
| 7405 | unsafe fn regionSubstitution 'arena (self: &mut Resolver 'arena, parameters: *RegionScope) -> RegionSubstitution { |
| 7406 | let count = parameters.entries.len; |
| 7407 | let arguments = try! alloc::allocRawSlice( |
| 7408 | self.arena, @sizeOf(?*unsafe types::Region), @alignOf(?*unsafe types::Region), count |
| 7409 | ) as *unsafe mut [?*unsafe types::Region]; |
| 7410 | for i in 0..count { |
| 7411 | set arguments[i] = nil; |
| 7412 | } |
| 7413 | return RegionSubstitution { parameters, arguments }; |
| 7414 | } |
| 7415 | |
| 7416 | /// Find a region's position among the region declarations in one scope. |
| 7417 | fn regionIndex(scope: &RegionScope, regionId: u32) -> ?u32 { |
| 7418 | match scope.declarations { |
| 7419 | case RegionDeclarations::Parameters(nodes) => { |
| 7420 | let mut index: u32 = 0; |
| 7421 | for node in nodes { |
| 7422 | let case ast::NodeValue::Region { .. } = node.value else continue; |
| 7423 | if node.id == regionId { |
| 7424 | return index; |
| 7425 | } |
| 7426 | set index += 1; |
| 7427 | } |
| 7428 | } |
| 7429 | case RegionDeclarations::Block(node) => { |
| 7430 | if node.id == regionId { |
| 7431 | return 0; |
| 7432 | } |
| 7433 | } |
| 7434 | } |
| 7435 | return nil; |
| 7436 | } |
| 7437 | |
| 7438 | /// Infer one region argument from a pair of reference classes. |
| 7439 | unsafe fn inferRegionClass 'arena ( |
| 7440 | self: &mut Resolver 'arena, map: &RegionSubstitution, |
| 7441 | expected: types::PointerClass, actual: types::PointerClass, site: *ast::Node |
| 7442 | ) throws (ResolveError) { |
| 7443 | let case types::PointerClass::Region(parameter) = expected else return; |
| 7444 | let index = regionIndex(map.parameters, parameter.id) else return; |
| 7445 | let case types::PointerClass::Region(argument) = actual |
| 7446 | else throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
| 7447 | if let previous = map.arguments[index]; previous.id <> argument.id { |
| 7448 | throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
| 7449 | } |
| 7450 | set map.arguments[index] = argument; |
| 7451 | } |
| 7452 | |
| 7453 | /// Infer regions through matching type structure without adding lifetime subtyping. |
| 7454 | unsafe fn inferRegionArguments 'arena ( |
| 7455 | self: &mut Resolver 'arena, map: &RegionSubstitution, expected: Type, actual: Type, site: *ast::Node |
| 7456 | ) throws (ResolveError) { |
| 7457 | match expected { |
| 7458 | case Type::Cell { class, permission, payload } => { |
| 7459 | let case Type::Cell { |
| 7460 | class: otherClass, permission: otherPermission, payload: other, |
| 7461 | } = actual else return; |
| 7462 | try inferRegionClass(self, map, class, otherClass, site); |
| 7463 | if let expectedPermission = permission { |
| 7464 | let actualPermission = otherPermission else return; |
| 7465 | try inferRegionClass( |
| 7466 | self, map, |
| 7467 | types::PointerClass::Region(expectedPermission), |
| 7468 | types::PointerClass::Region(actualPermission), |
| 7469 | site |
| 7470 | ); |
| 7471 | } |
| 7472 | try inferRegionArguments(self, map, *payload, *other, site); |
| 7473 | } |
| 7474 | case Type::Session(region) => { |
| 7475 | let case Type::Session(other) = actual else return; |
| 7476 | try inferRegionClass(self, map, types::PointerClass::Region(region), |
| 7477 | types::PointerClass::Region(other), site); |
| 7478 | } |
| 7479 | case Type::Pointer { class, target, .. } => { |
| 7480 | let case Type::Pointer { class: otherClass, target: otherTarget, .. } = actual else return; |
| 7481 | try inferRegionClass(self, map, class, otherClass, site); |
| 7482 | try inferRegionArguments(self, map, *target, *otherTarget, site); |
| 7483 | } |
| 7484 | case Type::Slice { class, item, .. } => { |
| 7485 | let case Type::Slice { class: otherClass, item: otherItem, .. } = actual else return; |
| 7486 | try inferRegionClass(self, map, class, otherClass, site); |
| 7487 | try inferRegionArguments(self, map, *item, *otherItem, site); |
| 7488 | } |
| 7489 | case Type::TraitObject { class, .. } => { |
| 7490 | if let case Type::TraitObject { class: otherClass, .. } = actual { |
| 7491 | try inferRegionClass(self, map, class, otherClass, site); |
| 7492 | } |
| 7493 | } |
| 7494 | case Type::Array(array) => { |
| 7495 | if let case Type::Array(other) = actual { |
| 7496 | try inferRegionArguments(self, map, *array.item, *other.item, site); |
| 7497 | } |
| 7498 | } |
| 7499 | case Type::Optional(inner) => { |
| 7500 | if let case Type::Optional(other) = actual { |
| 7501 | try inferRegionArguments(self, map, *inner, *other, site); |
| 7502 | } else { |
| 7503 | try inferRegionArguments(self, map, *inner, actual, site); |
| 7504 | } |
| 7505 | } |
| 7506 | case Type::Fn(info) => { |
| 7507 | let case Type::Fn(other) = actual else return; |
| 7508 | if info.paramTypes.len <> other.paramTypes.len or info.throwList.len <> other.throwList.len { |
| 7509 | return; |
| 7510 | } |
| 7511 | for parameter, i in info.paramTypes { |
| 7512 | try inferRegionArguments(self, map, *parameter, *other.paramTypes[i], site); |
| 7513 | } |
| 7514 | for error, i in info.throwList { |
| 7515 | try inferRegionArguments(self, map, *error, *other.throwList[i], site); |
| 7516 | } |
| 7517 | try inferRegionArguments(self, map, *info.returnType, *other.returnType, site); |
| 7518 | } |
| 7519 | case Type::Nominal(info) => { |
| 7520 | let applied = nominalApplication(info) else return; |
| 7521 | let case Type::Nominal(otherInfo) = actual else return; |
| 7522 | let other = nominalApplication(otherInfo) else return; |
| 7523 | if applied.base <> other.base { |
| 7524 | return; |
| 7525 | } |
| 7526 | for region, i in applied.arguments { |
| 7527 | try inferRegionClass(self, map, types::PointerClass::Region(region), |
| 7528 | types::PointerClass::Region(other.arguments[i]), site); |
| 7529 | } |
| 7530 | } |
| 7531 | else => { |
| 7532 | } |
| 7533 | } |
| 7534 | } |
| 7535 | |
| 7536 | /// Return whether a type uses one formal region as a cell permission. |
| 7537 | /// Unapplied nominals cannot capture a free formal region, so only exact |
| 7538 | /// applications need cycle marking and member traversal. |
| 7539 | unsafe fn typeHasFormalCellPermission 'arena ( |
| 7540 | self: &mut Resolver 'arena, |
| 7541 | ty: Type, |
| 7542 | permission: *unsafe types::Region, |
| 7543 | generation: u32, |
| 7544 | site: *ast::Node, |
| 7545 | ) -> bool throws (ResolveError) { |
| 7546 | match ty { |
| 7547 | case Type::Cell { permission: identity, payload, .. } => { |
| 7548 | if let cellPermission = identity; cellPermission == permission { |
| 7549 | return true; |
| 7550 | } |
| 7551 | return try typeHasFormalCellPermission( |
| 7552 | self, *payload, permission, generation, site, |
| 7553 | ); |
| 7554 | } |
| 7555 | case Type::Pointer { target, .. } => |
| 7556 | return try typeHasFormalCellPermission( |
| 7557 | self, *target, permission, generation, site, |
| 7558 | ), |
| 7559 | case Type::Slice { item, .. } => |
| 7560 | return try typeHasFormalCellPermission( |
| 7561 | self, *item, permission, generation, site, |
| 7562 | ), |
| 7563 | case Type::Array(array) => |
| 7564 | return try typeHasFormalCellPermission( |
| 7565 | self, *array.item, permission, generation, site, |
| 7566 | ), |
| 7567 | case Type::Optional(inner) => |
| 7568 | return try typeHasFormalCellPermission( |
| 7569 | self, *inner, permission, generation, site, |
| 7570 | ), |
| 7571 | case Type::Range { start, end } => { |
| 7572 | if let startType = start; |
| 7573 | try typeHasFormalCellPermission( |
| 7574 | self, *startType, permission, generation, site, |
| 7575 | ) |
| 7576 | { |
| 7577 | return true; |
| 7578 | } |
| 7579 | if let endType = end { |
| 7580 | return try typeHasFormalCellPermission( |
| 7581 | self, *endType, permission, generation, site, |
| 7582 | ); |
| 7583 | } |
| 7584 | return false; |
| 7585 | } |
| 7586 | case Type::Nominal(nominal) => { |
| 7587 | let applied = nominalApplication(nominal) else return false; |
| 7588 | try ensureNominalResolved(self, nominal, site); |
| 7589 | if not visitNominalApplication( |
| 7590 | applied, generation, RegionTypeRole::CellPermission, |
| 7591 | ) { |
| 7592 | return false; |
| 7593 | } |
| 7594 | match *nominal { |
| 7595 | case NominalType::Record(recordType) => { |
| 7596 | for field in recordType.fields { |
| 7597 | if try typeHasFormalCellPermission( |
| 7598 | self, field.fieldType, permission, generation, site, |
| 7599 | ) { |
| 7600 | return true; |
| 7601 | } |
| 7602 | } |
| 7603 | } |
| 7604 | case NominalType::Union(unionType) => { |
| 7605 | for variant in unionType.variants { |
| 7606 | if try typeHasFormalCellPermission( |
| 7607 | self, variant.valueType, permission, generation, site, |
| 7608 | ) { |
| 7609 | return true; |
| 7610 | } |
| 7611 | } |
| 7612 | } |
| 7613 | case NominalType::Placeholder(_), NominalType::Resolving(_), |
| 7614 | NominalType::Application(_) => |
| 7615 | panic "typeHasFormalCellPermission: unresolved application", |
| 7616 | } |
| 7617 | return false; |
| 7618 | } |
| 7619 | case Type::Fn(info) => { |
| 7620 | for parameter in info.paramTypes { |
| 7621 | if try typeHasFormalCellPermission( |
| 7622 | self, *parameter, permission, generation, site, |
| 7623 | ) { |
| 7624 | return true; |
| 7625 | } |
| 7626 | } |
| 7627 | for error in info.throwList { |
| 7628 | if try typeHasFormalCellPermission( |
| 7629 | self, *error, permission, generation, site, |
| 7630 | ) { |
| 7631 | return true; |
| 7632 | } |
| 7633 | } |
| 7634 | return try typeHasFormalCellPermission( |
| 7635 | self, *info.returnType, permission, generation, site, |
| 7636 | ); |
| 7637 | } |
| 7638 | else => return false, |
| 7639 | } |
| 7640 | } |
| 7641 | |
| 7642 | /// Return whether a function contract uses one formal cell permission. |
| 7643 | unsafe fn contractHasFormalCellPermission 'arena ( |
| 7644 | self: &mut Resolver 'arena, |
| 7645 | info: *FnType, |
| 7646 | receiver: ?Type, |
| 7647 | permission: *unsafe types::Region, |
| 7648 | generation: u32, |
| 7649 | site: *ast::Node, |
| 7650 | ) -> bool throws (ResolveError) { |
| 7651 | if let receiverType = receiver; |
| 7652 | try typeHasFormalCellPermission( |
| 7653 | self, receiverType, permission, generation, site, |
| 7654 | ) |
| 7655 | { |
| 7656 | return true; |
| 7657 | } |
| 7658 | for parameter in info.paramTypes { |
| 7659 | if try typeHasFormalCellPermission( |
| 7660 | self, *parameter, permission, generation, site, |
| 7661 | ) { |
| 7662 | return true; |
| 7663 | } |
| 7664 | } |
| 7665 | if try typeHasFormalCellPermission( |
| 7666 | self, *info.returnType, permission, generation, site, |
| 7667 | ) { |
| 7668 | return true; |
| 7669 | } |
| 7670 | for error in info.throwList { |
| 7671 | if try typeHasFormalCellPermission( |
| 7672 | self, *error, permission, generation, site, |
| 7673 | ) { |
| 7674 | return true; |
| 7675 | } |
| 7676 | } |
| 7677 | return false; |
| 7678 | } |
| 7679 | |
| 7680 | /// Validate region parents and keep distinct formal cell permissions injective. |
| 7681 | unsafe fn validateRegionArguments 'arena ( |
| 7682 | self: &mut Resolver 'arena, |
| 7683 | map: &RegionSubstitution, |
| 7684 | contract: ?*FnType, |
| 7685 | receiver: ?Type, |
| 7686 | site: *ast::Node, |
| 7687 | ) throws (ResolveError) { |
| 7688 | for parameter, i in map.parameters.entries { |
| 7689 | if map.arguments[i] == nil { |
| 7690 | throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
| 7691 | } |
| 7692 | } |
| 7693 | for parameter, i in map.parameters.entries { |
| 7694 | let parent = parameter.parent else continue; |
| 7695 | let index = regionIndex(map.parameters, parent.id) |
| 7696 | else panic "validateRegionArguments: unknown parent"; |
| 7697 | let parentArgument = map.arguments[index] |
| 7698 | else panic "validateRegionArguments: missing parent argument"; |
| 7699 | let argument = map.arguments[i] |
| 7700 | else panic "validateRegionArguments: missing argument"; |
| 7701 | if not types::regionContains(parentArgument, argument) { |
| 7702 | throw emitError(self, site, ErrorKind::RegionParent(parameter.name)); |
| 7703 | } |
| 7704 | } |
| 7705 | let info = contract else return; |
| 7706 | |
| 7707 | // Only colliding arguments need contract scans. Formal parameters bound the |
| 7708 | // candidates, while generation marks keep recursive applications finite. |
| 7709 | for parameter, i in map.parameters.entries { |
| 7710 | let argument = map.arguments[i] |
| 7711 | else panic "validateRegionArguments: missing argument"; |
| 7712 | let mut checked = false; |
| 7713 | for j in 0..i { |
| 7714 | let previousArgument = map.arguments[j] |
| 7715 | else panic "validateRegionArguments: missing previous argument"; |
| 7716 | if previousArgument <> argument { |
| 7717 | continue; |
| 7718 | } |
| 7719 | if not checked { |
| 7720 | let generation = nextNominalTraversalGeneration(self); |
| 7721 | set checked = true; |
| 7722 | if not try contractHasFormalCellPermission( |
| 7723 | self, info, receiver, parameter, generation, site, |
| 7724 | ) { |
| 7725 | break; |
| 7726 | } |
| 7727 | } |
| 7728 | let previous = map.parameters.entries[j]; |
| 7729 | let generation = nextNominalTraversalGeneration(self); |
| 7730 | if try contractHasFormalCellPermission( |
| 7731 | self, info, receiver, previous, generation, site, |
| 7732 | ) { |
| 7733 | throw emitError( |
| 7734 | self, site, ErrorKind::RegionInference(parameter.name), |
| 7735 | ); |
| 7736 | } |
| 7737 | } |
| 7738 | } |
| 7739 | } |
| 7740 | |
| 7741 | /// Substitute a reference's region while preserving its ownership class. |
| 7742 | unsafe fn substituteRegionClass(map: &RegionSubstitution, class: types::PointerClass) -> types::PointerClass { |
| 7743 | let case types::PointerClass::Region(region) = class else return class; |
| 7744 | let index = regionIndex(map.parameters, region.id) else return class; |
| 7745 | let argument = map.arguments[index] else panic "substituteRegionClass: missing argument"; |
| 7746 | return types::PointerClass::Region(argument); |
| 7747 | } |
| 7748 | |
| 7749 | /// Substitute free region arguments in a type without changing its runtime layout. |
| 7750 | unsafe fn substituteRegions 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, ty: Type) -> Type { |
| 7751 | match ty { |
| 7752 | case Type::Cell { class, permission, payload } => { |
| 7753 | let mut substitutedPermission = permission; |
| 7754 | if let region = permission { |
| 7755 | let case types::PointerClass::Region(argument) = substituteRegionClass( |
| 7756 | map, types::PointerClass::Region(region) |
| 7757 | ) else panic "substituteRegions: invalid permission class"; |
| 7758 | set substitutedPermission = argument; |
| 7759 | } |
| 7760 | return Type::Cell { |
| 7761 | class: substituteRegionClass(map, class), |
| 7762 | permission: substitutedPermission, |
| 7763 | payload: allocType(self, substituteRegions(self, map, *payload)), |
| 7764 | }; |
| 7765 | } |
| 7766 | case Type::Session(region) => { |
| 7767 | let index = regionIndex(map.parameters, region.id) else return ty; |
| 7768 | let argument = map.arguments[index] else panic "substituteRegions: missing session region"; |
| 7769 | return Type::Session(argument); |
| 7770 | } |
| 7771 | case Type::Pointer { class, target, mutable } => { |
| 7772 | let targetType = substituteRegions(self, map, *target); |
| 7773 | return Type::Pointer { class: substituteRegionClass(map, class), target: allocType(self, targetType), mutable }; |
| 7774 | } |
| 7775 | case Type::Slice { class, item, mutable } => { |
| 7776 | let itemType = substituteRegions(self, map, *item); |
| 7777 | return Type::Slice { class: substituteRegionClass(map, class), item: allocType(self, itemType), mutable }; |
| 7778 | } |
| 7779 | case Type::TraitObject { class, traitInfo, mutable } => |
| 7780 | return Type::TraitObject { class: substituteRegionClass(map, class), traitInfo, mutable }, |
| 7781 | case Type::Array(array) => { |
| 7782 | let itemType = substituteRegions(self, map, *array.item); |
| 7783 | return Type::Array(ArrayType { item: allocType(self, itemType), length: array.length }); |
| 7784 | } |
| 7785 | case Type::Optional(inner) => { |
| 7786 | let innerType = substituteRegions(self, map, *inner); |
| 7787 | return Type::Optional(allocType(self, innerType)); |
| 7788 | } |
| 7789 | case Type::Fn(info) => return Type::Fn(substituteFnRegions(self, map, info, info.regions)), |
| 7790 | case Type::Nominal(info) => { |
| 7791 | let applied = nominalApplication(info) else return ty; |
| 7792 | let arguments = regionSubstitution(self, applied.parameters); |
| 7793 | for region, i in applied.arguments { |
| 7794 | let class = substituteRegionClass(map, types::PointerClass::Region(region)); |
| 7795 | let case types::PointerClass::Region(argument) = class else panic; |
| 7796 | set arguments.arguments[i] = argument; |
| 7797 | } |
| 7798 | return Type::Nominal(internNominalApplication(self, applied.base, &arguments)); |
| 7799 | } |
| 7800 | else => return ty, |
| 7801 | } |
| 7802 | } |
| 7803 | |
| 7804 | /// Create a substituted signature with the specified remaining region binder. |
| 7805 | unsafe fn substituteFnRegions 'arena ( |
| 7806 | self: &mut Resolver 'arena, map: &RegionSubstitution, info: *FnType, regions: ?*RegionScope |
| 7807 | ) -> *FnType { |
| 7808 | let a = alloc::arenaAllocator(self.arena); |
| 7809 | let mut paramTypes: *mut [*Type] = &mut []; |
| 7810 | let mut throwList: *mut [*Type] = &mut []; |
| 7811 | for parameter in info.paramTypes { |
| 7812 | let ty = substituteRegions(self, map, *parameter); |
| 7813 | paramTypes.append(allocType(self, ty), a); |
| 7814 | } |
| 7815 | for error in info.throwList { |
| 7816 | let ty = substituteRegions(self, map, *error); |
| 7817 | throwList.append(allocType(self, ty), a); |
| 7818 | } |
| 7819 | let returnType = substituteRegions(self, map, *info.returnType); |
| 7820 | return allocFnType(self, FnType { |
| 7821 | regions, |
| 7822 | paramTypes: ¶mTypes[..], |
| 7823 | returnType: allocType(self, returnType), |
| 7824 | throwList: &throwList[..], |
| 7825 | isUnsafe: info.isUnsafe, |
| 7826 | }); |
| 7827 | } |
| 7828 | |
| 7829 | /// Preserve a call-scoped pointer class while inferring its region-bearing contents. |
| 7830 | /// Named reference regions are inferred from the source storage. |
| 7831 | unsafe fn regionInputHint 'arena (self: &mut Resolver 'arena, expected: Type) -> Type { |
| 7832 | if let case Type::Optional(inner) = expected { |
| 7833 | return regionInputHint(self, *inner); |
| 7834 | } |
| 7835 | match expected { |
| 7836 | case Type::Pointer { class, mutable, .. } => { |
| 7837 | if let case types::PointerClass::Region(_) = class { |
| 7838 | return Type::Unknown; |
| 7839 | } |
| 7840 | return Type::Pointer { class, target: allocType(self, Type::Unknown), mutable }; |
| 7841 | } |
| 7842 | case Type::Slice { class, mutable, .. } => { |
| 7843 | if let case types::PointerClass::Region(_) = class { |
| 7844 | return Type::Unknown; |
| 7845 | } |
| 7846 | return Type::Slice { class, item: allocType(self, Type::Unknown), mutable }; |
| 7847 | } |
| 7848 | else => return Type::Unknown, |
| 7849 | } |
| 7850 | } |
| 7851 | |
| 7852 | /// Infer a source function's region arguments from its call inputs. |
| 7853 | unsafe fn instantiateCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType) -> *FnType |
| 7854 | throws (ResolveError) |
| 7855 | { |
| 7856 | let parameters = info.regions else return info; |
| 7857 | if call.args.len <> info.paramTypes.len { |
| 7858 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 7859 | expected: info.paramTypes.len, actual: call.args.len, |
| 7860 | })); |
| 7861 | } |
| 7862 | let map = regionSubstitution(self, parameters); |
| 7863 | for argument, i in call.args { |
| 7864 | let expected = *info.paramTypes[i]; |
| 7865 | if containsRegion(expected) { |
| 7866 | let actual = try visit(self, argument, regionInputHint(self, expected)); |
| 7867 | try inferRegionArguments(self, &map, expected, actual, argument); |
| 7868 | } |
| 7869 | } |
| 7870 | try validateRegionArguments(self, &map, info, nil, node); |
| 7871 | return substituteFnRegions(self, &map, info, nil); |
| 7872 | } |
| 7873 | |
| 7874 | /// Infer a method's region arguments from its receiver and call arguments. |
| 7875 | unsafe fn instantiateMethodCall 'arena ( |
| 7876 | self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, |
| 7877 | receiver: *ast::Node, receiverType: Type, method: *unsafe MethodEntry |
| 7878 | ) -> *FnType throws (ResolveError) { |
| 7879 | let parameters = method.fnType.regions else return method.fnType; |
| 7880 | if call.args.len <> method.fnType.paramTypes.len { |
| 7881 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 7882 | expected: method.fnType.paramTypes.len, actual: call.args.len, |
| 7883 | })); |
| 7884 | } |
| 7885 | let map = regionSubstitution(self, parameters); |
| 7886 | try inferRegionArguments(self, &map, method.concreteType, receiverType, receiver); |
| 7887 | for argument, i in call.args { |
| 7888 | let expected = *method.fnType.paramTypes[i]; |
| 7889 | if containsRegion(expected) { |
| 7890 | let actual = try visit(self, argument, regionInputHint(self, expected)); |
| 7891 | try inferRegionArguments(self, &map, expected, actual, argument); |
| 7892 | } |
| 7893 | } |
| 7894 | try validateRegionArguments( |
| 7895 | self, &map, method.fnType, method.concreteType, node, |
| 7896 | ); |
| 7897 | return substituteFnRegions(self, &map, method.fnType, nil); |
| 7898 | } |
| 7899 | |
| 7900 | /// Apply explicit current-scope regions to a function or nominal type. |
| 7901 | unsafe fn resolveRegionApply 'arena ( |
| 7902 | self: &mut Resolver 'arena, node: *ast::Node, value: *ast::Node, regions: *[*ast::Node] |
| 7903 | ) -> Type throws (ResolveError) { |
| 7904 | let ty = try infer(self, value); |
| 7905 | if let case Type::Nominal(base) = ty { |
| 7906 | let symbol = symbolFor(self, value) else throw emitError(self, node, ErrorKind::CannotInferType); |
| 7907 | let case SymbolData::Type(_) = symbol.data else throw emitError(self, node, ErrorKind::CannotInferType); |
| 7908 | let applied = try applyNominalRegions(self, base, regions, node); |
| 7909 | try ensureNominalResolved(self, applied, node); |
| 7910 | setNodeSymbol(self, node, symbol); |
| 7911 | return setNodeType(self, node, Type::Nominal(applied)); |
| 7912 | } |
| 7913 | let case Type::Fn(info) = ty else throw emitError(self, node, ErrorKind::CannotInferType); |
| 7914 | let mut count: u32 = 0; |
| 7915 | if let scope = info.regions { |
| 7916 | set count = scope.entries.len; |
| 7917 | } |
| 7918 | if count <> regions.len { |
| 7919 | throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len })); |
| 7920 | } |
| 7921 | let scope = info.regions else panic "resolveRegionApply: empty region application"; |
| 7922 | let map = regionSubstitution(self, scope); |
| 7923 | for region, i in regions { |
| 7924 | set map.arguments[i] = try resolveRegion(self, region); |
| 7925 | } |
| 7926 | try validateRegionArguments(self, &map, info, nil, node); |
| 7927 | let applied = substituteFnRegions(self, &map, info, nil); |
| 7928 | if let symbol = symbolFor(self, value) { |
| 7929 | setNodeSymbol(self, node, symbol); |
| 7930 | } |
| 7931 | return setNodeType(self, node, Type::Fn(applied)); |
| 7932 | } |
| 7933 | |
| 7934 | /// Validate call arguments against a function type: check argument count, |
| 7935 | /// type-check each argument, and verify that throwing functions use `try`. |
| 7936 | unsafe fn checkCallArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
| 7937 | throws (ResolveError) |
| 7938 | { |
| 7939 | if ctx == CallCtx::Normal and info.throwList.len > 0 { |
| 7940 | throw emitError(self, node, ErrorKind::MissingTry); |
| 7941 | } |
| 7942 | if call.args.len <> info.paramTypes.len as u32 { |
| 7943 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 7944 | expected: info.paramTypes.len as u32, |
| 7945 | actual: call.args.len, |
| 7946 | })); |
| 7947 | } |
| 7948 | for argNode, i in call.args { |
| 7949 | let expectedTy = *info.paramTypes[i]; |
| 7950 | |
| 7951 | try checkAssignable(self, argNode, expectedTy); |
| 7952 | } |
| 7953 | } |
| 7954 | |
| 7955 | /// Return whether a value can be discarded by bulk arena reclamation. |
| 7956 | /// Pointer lifetimes are checked when their values are constructed. |
| 7957 | unsafe fn isBulkDiscardable(ty: Type) -> bool { |
| 7958 | match ty { |
| 7959 | case Type::Void, Type::Bool, Type::U8, Type::U16, Type::U32, Type::U64, |
| 7960 | Type::I8, Type::I16, Type::I32, Type::I64, Type::Fn(_) => return true, |
| 7961 | case Type::Pointer { .. }, Type::Slice { .. }, Type::TraitObject { .. } => return true, |
| 7962 | case Type::Cell { .. } => return true, |
| 7963 | case Type::Array(array) => return isBulkDiscardable(*array.item), |
| 7964 | case Type::Optional(inner) => return isBulkDiscardable(*inner), |
| 7965 | case Type::Nominal(NominalType::Record(recordType)) => { |
| 7966 | if recordType.declaredLinear { |
| 7967 | return false; |
| 7968 | } |
| 7969 | for field in recordType.fields { |
| 7970 | if not isBulkDiscardable(field.fieldType) { |
| 7971 | return false; |
| 7972 | } |
| 7973 | } |
| 7974 | return true; |
| 7975 | } |
| 7976 | case Type::Nominal(NominalType::Union(unionType)) => { |
| 7977 | if unionType.declaredLinear { |
| 7978 | return false; |
| 7979 | } |
| 7980 | for variant in unionType.variants { |
| 7981 | if not isBulkDiscardable(variant.valueType) { |
| 7982 | return false; |
| 7983 | } |
| 7984 | } |
| 7985 | return true; |
| 7986 | } |
| 7987 | else => return false, |
| 7988 | } |
| 7989 | } |
| 7990 | |
| 7991 | /// Compute the end of an aligned layout within the allocator's byte-count range. |
| 7992 | fn allocationLayoutEnd(offset: u64, layout: Layout) -> ?u64 { |
| 7993 | let mut aligned = offset; |
| 7994 | if layout.alignment > 0 { |
| 7995 | let mask = (layout.alignment - 1) as u64; |
| 7996 | set aligned = (offset + mask) & ~mask; |
| 7997 | } |
| 7998 | let end = aligned + layout.size as u64; |
| 7999 | if end > 4294967295 { |
| 8000 | return nil; |
| 8001 | } |
| 8002 | return end; |
| 8003 | } |
| 8004 | |
| 8005 | /// Check allocation layout arithmetic independently of the stored narrow offsets. |
| 8006 | unsafe fn hasAllocationLayout(ty: Type) -> bool { |
| 8007 | let layout = getTypeLayout(ty); |
| 8008 | match ty { |
| 8009 | case Type::Cell { .. } => return true, |
| 8010 | case Type::Array(array) => { |
| 8011 | if not hasAllocationLayout(*array.item) { |
| 8012 | return false; |
| 8013 | } |
| 8014 | let item = getTypeLayout(*array.item); |
| 8015 | return item.size as u64 * array.length as u64 == layout.size as u64; |
| 8016 | } |
| 8017 | case Type::Optional(inner) => { |
| 8018 | if not hasAllocationLayout(*inner) { |
| 8019 | return false; |
| 8020 | } |
| 8021 | if isNullableType(*inner) { |
| 8022 | return true; |
| 8023 | } |
| 8024 | let end = allocationLayoutEnd(1, getTypeLayout(*inner)) else return false; |
| 8025 | let total = allocationLayoutEnd(end, Layout { size: 0, alignment: layout.alignment }) else return false; |
| 8026 | return total == layout.size as u64; |
| 8027 | } |
| 8028 | case Type::Nominal(NominalType::Record(recordType)) => { |
| 8029 | let mut offset: u64 = 0; |
| 8030 | for field in recordType.fields { |
| 8031 | if not hasAllocationLayout(field.fieldType) { |
| 8032 | return false; |
| 8033 | } |
| 8034 | let fieldLayout = getTypeLayout(field.fieldType); |
| 8035 | let end = allocationLayoutEnd(offset, fieldLayout) else return false; |
| 8036 | let start = end - fieldLayout.size as u64; |
| 8037 | if start > 2147483647 or field.offset < 0 or start <> field.offset as u64 { |
| 8038 | return false; |
| 8039 | } |
| 8040 | set offset = end; |
| 8041 | } |
| 8042 | let total = allocationLayoutEnd(offset, Layout { size: 0, alignment: layout.alignment }) else return false; |
| 8043 | return total == layout.size as u64; |
| 8044 | } |
| 8045 | case Type::Nominal(NominalType::Union(unionType)) => { |
| 8046 | let mut payloadSize: u32 = 0; |
| 8047 | let mut alignment: u32 = 1; |
| 8048 | for variant in unionType.variants { |
| 8049 | if not hasAllocationLayout(variant.valueType) { |
| 8050 | return false; |
| 8051 | } |
| 8052 | let item = getTypeLayout(variant.valueType); |
| 8053 | set payloadSize = max(payloadSize, item.size); |
| 8054 | set alignment = max(alignment, item.alignment); |
| 8055 | } |
| 8056 | let end = allocationLayoutEnd(1, Layout { size: payloadSize, alignment }) else return false; |
| 8057 | let total = allocationLayoutEnd(end, Layout { size: 0, alignment }) else return false; |
| 8058 | return total == layout.size as u64 and end - payloadSize as u64 == unionType.valOffset as u64; |
| 8059 | } |
| 8060 | else => return true, |
| 8061 | } |
| 8062 | } |
| 8063 | |
| 8064 | /// Validate the reservation ABI used by typed session allocation. |
| 8065 | unsafe fn sessionRuntime 'arena ( |
| 8066 | self: &mut Resolver 'arena, node: *ast::Node, slice: bool |
| 8067 | ) -> TraitMethod |
| 8068 | throws (ResolveError) |
| 8069 | { |
| 8070 | let allocTrait = allocationSymbol(self, "Alloc") |
| 8071 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8072 | let case SymbolData::Trait(allocInfo) = allocTrait.data |
| 8073 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8074 | let name = "reserveSlice" if slice else "reserve"; |
| 8075 | let method = findTraitMethod(&allocInfo.methods[..], name) |
| 8076 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8077 | let error = allocationSymbol(self, "AllocError") |
| 8078 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8079 | let case SymbolData::Type(errorType) = error.data |
| 8080 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8081 | let count: u32 = 3 if slice else 2; |
| 8082 | let info = method.fnType; |
| 8083 | if info.regions <> nil or not info.isUnsafe or info.paramTypes.len <> count or info.throwList.len <> 1 { |
| 8084 | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8085 | } |
| 8086 | if not typesEqual(*info.throwList[0], Type::Nominal(errorType)) { |
| 8087 | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8088 | } |
| 8089 | for i in 0..count { |
| 8090 | if *info.paramTypes[i] <> Type::U32 { |
| 8091 | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8092 | } |
| 8093 | } |
| 8094 | let expected = Type::Slice { class: types::PointerClass::Unsafe, item: allocType(self, Type::Opaque), mutable: true } |
| 8095 | if slice else Type::Pointer { |
| 8096 | class: types::PointerClass::Unsafe, target: allocType(self, Type::Opaque), mutable: true |
| 8097 | }; |
| 8098 | if not typesEqual(*info.returnType, expected) { |
| 8099 | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 8100 | } |
| 8101 | return method; |
| 8102 | } |
| 8103 | |
| 8104 | /// Check initialized session allocation and retain its source region in the result. |
| 8105 | unsafe fn resolveSessionAllocation 'arena ( |
| 8106 | self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, access: ast::Access, |
| 8107 | region: *unsafe types::Region, ctx: CallCtx, hint: Type |
| 8108 | ) -> Type throws (ResolveError) { |
| 8109 | let name = try nodeName(self, access.child); |
| 8110 | let mut kind = SessionAllocationKind::New; |
| 8111 | if mem::eq(name, "copy") { |
| 8112 | set kind = SessionAllocationKind::Copy; |
| 8113 | } |
| 8114 | else if mem::eq(name, "fill") { |
| 8115 | set kind = SessionAllocationKind::Fill; |
| 8116 | } |
| 8117 | else if not mem::eq(name, "new") { |
| 8118 | throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(name)); |
| 8119 | } |
| 8120 | let count: u32 = 2 if kind == SessionAllocationKind::Fill else 1; |
| 8121 | if call.args.len <> count { |
| 8122 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { expected: count, actual: call.args.len })); |
| 8123 | } |
| 8124 | let slice = kind <> SessionAllocationKind::New; |
| 8125 | let mut itemHint = Type::Unknown; |
| 8126 | if kind == SessionAllocationKind::New { |
| 8127 | if let case Type::Pointer { target, .. } = hint { |
| 8128 | set itemHint = *target; |
| 8129 | } |
| 8130 | } else if kind == SessionAllocationKind::Fill { |
| 8131 | if let case Type::Slice { item, .. } = hint { |
| 8132 | set itemHint = *item; |
| 8133 | } |
| 8134 | } else { |
| 8135 | set itemHint = Type::Slice { |
| 8136 | class: types::PointerClass::Ref, item: allocType(self, Type::Unknown), mutable: false, |
| 8137 | }; |
| 8138 | } |
| 8139 | let valueType = try visit(self, call.args[0], itemHint); |
| 8140 | let mut itemType = valueType; |
| 8141 | let mut parameter = valueType; |
| 8142 | if kind == SessionAllocationKind::Copy { |
| 8143 | let case Type::Slice { item, .. } = valueType |
| 8144 | else throw emitError(self, call.args[0], ErrorKind::ExpectedIndexable); |
| 8145 | set itemType = *item; |
| 8146 | set parameter = Type::Slice { class: types::PointerClass::Ref, item, mutable: false }; |
| 8147 | } |
| 8148 | if not isTypeInferrable(itemType) or itemType == Type::Void or itemType == Type::Opaque { |
| 8149 | throw emitError(self, call.args[0], ErrorKind::CannotInferType); |
| 8150 | } |
| 8151 | try ensureStorableType(self, call.args[0], itemType); |
| 8152 | try ensureTypeResolved(self, itemType, call.args[0]); |
| 8153 | try validateRegionStorage(self, call.args[0], itemType, region); |
| 8154 | if not isBulkDiscardable(itemType) or (slice and not isCopy(itemType)) { |
| 8155 | throw emitError(self, call.args[0], ErrorKind::InvalidAllocationValue); |
| 8156 | } |
| 8157 | if not hasAllocationLayout(itemType) { |
| 8158 | throw emitError(self, call.args[0], ErrorKind::InvalidAllocationLayout); |
| 8159 | } |
| 8160 | let runtime = try sessionRuntime(self, node, slice); |
| 8161 | let runtimeType = runtime.fnType; |
| 8162 | let item = allocType(self, itemType); |
| 8163 | let result = Type::Slice { class: types::PointerClass::Region(region), item, mutable: true } |
| 8164 | if slice else Type::Pointer { |
| 8165 | class: types::PointerClass::Region(region), target: item, mutable: true |
| 8166 | }; |
| 8167 | let a = alloc::arenaAllocator(self.arena); |
| 8168 | let mut parameters: *mut [*Type] = &mut []; |
| 8169 | parameters.append(allocType(self, parameter), a); |
| 8170 | if kind == SessionAllocationKind::Fill { |
| 8171 | parameters.append(allocType(self, Type::U32), a); |
| 8172 | } |
| 8173 | let info = allocFnType(self, FnType { |
| 8174 | regions: nil, paramTypes: ¶meters[..], returnType: allocType(self, result), |
| 8175 | throwList: runtimeType.throwList, isUnsafe: false, |
| 8176 | }); |
| 8177 | try checkCallArgs(self, node, call, info, ctx); |
| 8178 | setNodeType(self, call.callee, Type::Fn(info)); |
| 8179 | let allocTrait = allocationSymbol(self, "Alloc") else panic; |
| 8180 | let case SymbolData::Trait(traitInfo) = allocTrait.data else panic; |
| 8181 | set self.nodeData.entries[node.id].extra = NodeExtra::SessionAllocation(SessionAllocation { |
| 8182 | kind, item, traitInfo, methodIndex: runtime.index, |
| 8183 | }); |
| 8184 | return setNodeType(self, node, result); |
| 8185 | } |
| 8186 | |
| 8187 | /// Require a complete, bulk-discardable cell payload, and Copy when unassociated. |
| 8188 | unsafe fn validateCellPayload 'arena ( |
| 8189 | self: &mut Resolver 'arena, |
| 8190 | node: *ast::Node, |
| 8191 | payload: Type, |
| 8192 | permission: ?*unsafe types::Region, |
| 8193 | ) throws (ResolveError) { |
| 8194 | try ensureStorableType(self, node, payload); |
| 8195 | try ensureTypeResolved(self, payload, node); |
| 8196 | if not isTypeInferrable(payload) or payload == Type::Void or payload == Type::Opaque |
| 8197 | or (permission == nil and not isCopy(payload)) or not isBulkDiscardable(payload) |
| 8198 | { |
| 8199 | throw emitError(self, node, ErrorKind::InvalidCellPayload); |
| 8200 | } |
| 8201 | if not hasAllocationLayout(payload) { |
| 8202 | throw emitError(self, node, ErrorKind::InvalidAllocationLayout); |
| 8203 | } |
| 8204 | } |
| 8205 | |
| 8206 | /// Analyze a function call expression. |
| 8207 | unsafe fn resolveCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, ctx: CallCtx, hint: Type) -> Type |
| 8208 | throws (ResolveError) |
| 8209 | { |
| 8210 | // Intercept method calls on slices before inferring the callee. |
| 8211 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
| 8212 | let parentTy = try infer(self, access.parent); |
| 8213 | if isUnsafePointerType(parentTy) { |
| 8214 | try requireUnsafe(self, access.parent); |
| 8215 | } |
| 8216 | let subjectTy = autoDeref(parentTy); |
| 8217 | if let case Type::Session(region) = subjectTy { |
| 8218 | return try resolveSessionAllocation(self, node, call, access, region, ctx, hint); |
| 8219 | } |
| 8220 | |
| 8221 | if let case Type::Slice { item, mutable, .. } = subjectTy { |
| 8222 | let methodName = try nodeName(self, access.child); |
| 8223 | if methodName == "append" { |
| 8224 | return try resolveSliceAppend( |
| 8225 | self, node, access.parent, parentTy, call.args, item, mutable |
| 8226 | ); |
| 8227 | } |
| 8228 | if methodName == "delete" { |
| 8229 | return try resolveSliceDelete( |
| 8230 | self, node, access.parent, call.args, item, mutable |
| 8231 | ); |
| 8232 | } |
| 8233 | } |
| 8234 | } |
| 8235 | let calleeTy = try visit(self, call.callee, hint); |
| 8236 | if let case Type::Fn(info) = calleeTy { |
| 8237 | try checkUnsafeCall(self, call.callee, info); |
| 8238 | } |
| 8239 | |
| 8240 | // Check if callee is a union variant and dispatch to constructor handler. |
| 8241 | // TODO: Move this out. We should decide on this earlier, based on the callee. |
| 8242 | if let calleeSym = symbolFor(self, call.callee) { |
| 8243 | if let case SymbolData::Variant { decl, .. } = calleeSym.data { |
| 8244 | // TODO: Don't pass the callee type, pass the union type by getting it from |
| 8245 | // the symbol. |
| 8246 | let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid variant type"; |
| 8247 | return try resolveUnionConstructorCall(self, node, call, ty); |
| 8248 | } |
| 8249 | // Check if callee is an unlabeled record type for constructor call syntax. |
| 8250 | if let case SymbolData::Type(_) = calleeSym.data { |
| 8251 | let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid type callee"; |
| 8252 | try requireNominalArguments(self, ty, call.callee); |
| 8253 | // Ensure the record body is resolved before checking if labeled. |
| 8254 | try ensureNominalResolved(self, ty, call.callee); |
| 8255 | if let case NominalType::Record(recInfo) = *ty { |
| 8256 | if not recInfo.labeled { |
| 8257 | return try resolveRecordConstructorCall(self, node, call, ty); |
| 8258 | } |
| 8259 | } |
| 8260 | } |
| 8261 | } |
| 8262 | |
| 8263 | // Check if we have a trait method call, ie. callee is a trait object. |
| 8264 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
| 8265 | let mut parentTy = Type::Unknown; |
| 8266 | if let t = typeFor(self, access.parent) { |
| 8267 | set parentTy = t; |
| 8268 | } |
| 8269 | let subjectTy = autoDeref(parentTy); |
| 8270 | |
| 8271 | if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy { |
| 8272 | let methodName = try nodeName(self, access.child); |
| 8273 | let method = findTraitMethod(&traitInfo.methods[..], methodName) |
| 8274 | else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName)); |
| 8275 | |
| 8276 | // Reject mutable-receiver methods called on immutable trait objects. |
| 8277 | if method.mutable { |
| 8278 | if not objMutable or not try canMutateThrough(self, access.parent) { |
| 8279 | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
| 8280 | } |
| 8281 | } |
| 8282 | let applied = try instantiateCall(self, node, call, method.fnType); |
| 8283 | try resolveInlineTypeViews(self, *applied.returnType, node); |
| 8284 | try checkCallArgs(self, node, call, applied, ctx); |
| 8285 | setNodeType(self, call.callee, Type::Fn(applied)); |
| 8286 | setTraitMethodCall(self, node, traitInfo, method.index); |
| 8287 | |
| 8288 | return setNodeType(self, node, *applied.returnType); |
| 8289 | } |
| 8290 | |
| 8291 | // Check for a standalone method call on a concrete type. |
| 8292 | if let case Type::Nominal(_) = subjectTy { |
| 8293 | let methodName = try nodeName(self, access.child); |
| 8294 | if let method = findMethod(self, subjectTy, methodName) { |
| 8295 | // Reject mutable-receiver methods on immutable bindings. |
| 8296 | // If the parent is already a mutable pointer, the receiver is fine. |
| 8297 | // Otherwise, check that the parent can yield a mutable borrow. |
| 8298 | if method.mutable { |
| 8299 | if not try canMutateThrough(self, access.parent) { |
| 8300 | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
| 8301 | } |
| 8302 | } |
| 8303 | // Check arguments (excluding receiver). |
| 8304 | let applied = try instantiateMethodCall( |
| 8305 | self, node, call, access.parent, subjectTy, method |
| 8306 | ); |
| 8307 | try resolveInlineTypeViews(self, *applied.returnType, node); |
| 8308 | try checkCallArgs(self, node, call, applied, ctx); |
| 8309 | setNodeType(self, call.callee, Type::Fn(applied)); |
| 8310 | set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method }; |
| 8311 | |
| 8312 | return setNodeType(self, node, *applied.returnType); |
| 8313 | } |
| 8314 | } |
| 8315 | } |
| 8316 | let case Type::Fn(info) = calleeTy else { |
| 8317 | throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch { |
| 8318 | expected: Type::Unknown, |
| 8319 | actual: calleeTy, |
| 8320 | })); |
| 8321 | }; |
| 8322 | let applied = try instantiateCall(self, node, call, info); |
| 8323 | try resolveInlineTypeViews(self, *applied.returnType, node); |
| 8324 | try checkCallArgs(self, node, call, applied, ctx); |
| 8325 | // Associate function type to callee. |
| 8326 | setNodeType(self, call.callee, Type::Fn(applied)); |
| 8327 | |
| 8328 | // Associate return type to call. |
| 8329 | return setNodeType(self, node, *applied.returnType); |
| 8330 | } |
| 8331 | |
| 8332 | /// Check labeled record fields against the slice allocator layout and callback ABI. |
| 8333 | fn isSliceAllocator(fields: &[RecordField]) -> bool { |
| 8334 | if fields.len <> 2 { |
| 8335 | return false; |
| 8336 | } |
| 8337 | let func = fields[0]; |
| 8338 | let ctx = fields[1]; |
| 8339 | let funcName = func.name else return false; |
| 8340 | let ctxName = ctx.name else return false; |
| 8341 | if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or |
| 8342 | func.offset <> 0 or ctx.offset <> 8 |
| 8343 | { |
| 8344 | return false; |
| 8345 | } |
| 8346 | let case Type::Fn(callback) = func.fieldType else return false; |
| 8347 | let case Type::Pointer { target, .. } = ctx.fieldType else return false; |
| 8348 | if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 { |
| 8349 | return false; |
| 8350 | } |
| 8351 | if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or |
| 8352 | *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32 |
| 8353 | { |
| 8354 | return false; |
| 8355 | } |
| 8356 | let case Type::Pointer { class, target: result, mutable } = *callback.returnType |
| 8357 | else return false; |
| 8358 | return class == types::PointerClass::Owned and mutable and *result == Type::Opaque; |
| 8359 | } |
| 8360 | |
| 8361 | /// Resolve `slice.append(val, allocator)`. |
| 8362 | unsafe fn resolveSliceAppend 'arena ( |
| 8363 | self: &mut Resolver 'arena, |
| 8364 | node: *ast::Node, |
| 8365 | parent: *ast::Node, |
| 8366 | parentType: Type, |
| 8367 | args: *[*ast::Node], |
| 8368 | elemType: *Type, |
| 8369 | mutable: bool |
| 8370 | ) -> Type throws (ResolveError) { |
| 8371 | if not mutable { |
| 8372 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 8373 | } |
| 8374 | if args.len <> 2 { |
| 8375 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 8376 | expected: 2, |
| 8377 | actual: args.len as u32, |
| 8378 | })); |
| 8379 | } |
| 8380 | // First argument must be assignable to the element type. |
| 8381 | try checkAssignable(self, args[0], *elemType); |
| 8382 | // The allocator stores its callback and context at fixed offsets. |
| 8383 | let allocatorTy = try infer(self, args[1]); |
| 8384 | if let case Type::Nominal(info) = allocatorTy { |
| 8385 | try ensureNominalResolved(self, info, args[1]); |
| 8386 | } |
| 8387 | let mut validAllocator = false; |
| 8388 | if let case Type::Nominal(NominalType::Record(rec)) = allocatorTy; rec.labeled { |
| 8389 | set validAllocator = isSliceAllocator(&rec.fields[..]); |
| 8390 | } |
| 8391 | if not validAllocator { |
| 8392 | throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator); |
| 8393 | } |
| 8394 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
| 8395 | |
| 8396 | // Return the parent's type so the caller can rebind: |
| 8397 | return setNodeType(self, node, parentType); |
| 8398 | } |
| 8399 | |
| 8400 | /// Resolve `slice.delete(index)`. |
| 8401 | unsafe fn resolveSliceDelete 'arena ( |
| 8402 | self: &mut Resolver 'arena, |
| 8403 | node: *ast::Node, |
| 8404 | parent: *ast::Node, |
| 8405 | args: *[*ast::Node], |
| 8406 | elemType: *Type, |
| 8407 | mutable: bool |
| 8408 | ) -> Type throws (ResolveError) { |
| 8409 | if not mutable { |
| 8410 | throw emitError(self, parent, ErrorKind::ImmutableBinding); |
| 8411 | } |
| 8412 | if args.len <> 1 { |
| 8413 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 8414 | expected: 1, |
| 8415 | actual: args.len as u32, |
| 8416 | })); |
| 8417 | } |
| 8418 | try checkAssignable(self, args[0], Type::U32); |
| 8419 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType }; |
| 8420 | |
| 8421 | return setNodeType(self, node, Type::Void); |
| 8422 | } |
| 8423 | |
| 8424 | /// Analyze an assignment expression. |
| 8425 | unsafe fn resolveAssign 'arena (self: &mut Resolver 'arena, node: *ast::Node, assign: ast::Assign) -> Type |
| 8426 | throws (ResolveError) |
| 8427 | { |
| 8428 | // Slice assignment: `slice[range] = value`. |
| 8429 | if let case ast::NodeValue::Subscript { container, index } = assign.left.value { |
| 8430 | if let case ast::NodeValue::Range(range) = index.value { |
| 8431 | try infer(self, index); |
| 8432 | let containerTy = try infer(self, container); |
| 8433 | let cellTy = try inferCellPayload(self, container); |
| 8434 | if cellTy == nil and not try canMutateThrough(self, container) { |
| 8435 | throw emitError(self, container, ErrorKind::ImmutableBinding); |
| 8436 | } |
| 8437 | let subjectTy = autoDeref(containerTy); |
| 8438 | try checkSliceRangeIndices(self, range); |
| 8439 | |
| 8440 | let info = sliceRangeInfo(subjectTy) |
| 8441 | else throw emitError(self, container, ErrorKind::ExpectedIndexable); |
| 8442 | if not info.mutable { |
| 8443 | throw emitError(self, container, ErrorKind::ImmutableBinding); |
| 8444 | } |
| 8445 | if let capacity = info.capacity { |
| 8446 | try validateArraySliceBounds(self, range, capacity, node); |
| 8447 | } |
| 8448 | let item = info.itemType; |
| 8449 | // RHS is either a fill value or a source slice. |
| 8450 | let rhsTy = try infer(self, assign.right); |
| 8451 | if let case Type::Slice { item: sourceItem, .. } = rhsTy { |
| 8452 | if *sourceItem <> *item { |
| 8453 | throw emitTypeMismatch( |
| 8454 | self, |
| 8455 | assign.right, |
| 8456 | TypeMismatch { expected: *item, actual: *sourceItem }, |
| 8457 | ); |
| 8458 | } |
| 8459 | } else { |
| 8460 | try checkAssignable(self, assign.right, *item); |
| 8461 | } |
| 8462 | if let controlled = cellTy { |
| 8463 | let case Type::Cell { class, .. } = controlled |
| 8464 | else panic "resolveAssign: invalid cell range payload"; |
| 8465 | if let case types::PointerClass::Region(region) = class { |
| 8466 | try validateRegionStorage(self, assign.right, *item, region); |
| 8467 | } else if class == types::PointerClass::Owned |
| 8468 | and try containsStorageRegion(self, assign.right, *item) |
| 8469 | { |
| 8470 | throw emitError(self, assign.right, ErrorKind::InvalidCellPayload); |
| 8471 | } |
| 8472 | } else { |
| 8473 | try validateRegionalStore(self, assign.left, assign.right, *item); |
| 8474 | } |
| 8475 | setSliceRangeInfo(self, node, info); |
| 8476 | setNodeType(self, assign.left, *item); |
| 8477 | |
| 8478 | return setNodeType(self, node, Type::Void); |
| 8479 | } |
| 8480 | } |
| 8481 | let leftTy = try infer(self, assign.left); |
| 8482 | |
| 8483 | if let cellTy = try inferCellPayload(self, assign.left) { |
| 8484 | let case Type::Cell { class, .. } = cellTy |
| 8485 | else panic "resolveAssign: invalid cell payload"; |
| 8486 | try checkAssignable(self, assign.right, leftTy); |
| 8487 | if let case types::PointerClass::Region(region) = class { |
| 8488 | try validateRegionStorage(self, assign.right, leftTy, region); |
| 8489 | } else if class == types::PointerClass::Owned |
| 8490 | and try containsStorageRegion(self, assign.right, leftTy) |
| 8491 | { |
| 8492 | throw emitError(self, assign.right, ErrorKind::InvalidCellPayload); |
| 8493 | } |
| 8494 | return setNodeType(self, node, leftTy); |
| 8495 | } |
| 8496 | |
| 8497 | // Check if the left-hand side can be assigned to by checking if it's a mutable location. |
| 8498 | if not try canBorrowMutFrom(self, assign.left) { |
| 8499 | throw emitError(self, assign.left, ErrorKind::ImmutableBinding); |
| 8500 | } |
| 8501 | try checkAssignable(self, assign.right, leftTy); |
| 8502 | try validateRegionalStore(self, assign.left, assign.right, leftTy); |
| 8503 | |
| 8504 | return setNodeType(self, node, leftTy); |
| 8505 | } |
| 8506 | |
| 8507 | /// Construct complete range metadata for an array or slice type. |
| 8508 | /// Callers check array place access and select the resulting borrow access. |
| 8509 | fn sliceRangeInfo(ty: Type) -> ?SliceRangeInfo { |
| 8510 | match ty { |
| 8511 | case Type::Slice { item, mutable, .. } => |
| 8512 | return SliceRangeInfo { itemType: item, mutable, capacity: nil }, |
| 8513 | case Type::Array(array) => |
| 8514 | return SliceRangeInfo { itemType: array.item, mutable: true, capacity: array.length }, |
| 8515 | else => return nil, |
| 8516 | } |
| 8517 | } |
| 8518 | |
| 8519 | /// Ensure slice range bounds are valid `u32` values. |
| 8520 | unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) { |
| 8521 | if let start = range.start { |
| 8522 | try checkIndex(self, start); |
| 8523 | } |
| 8524 | if let end = range.end { |
| 8525 | try checkIndex(self, end); |
| 8526 | } |
| 8527 | } |
| 8528 | |
| 8529 | /// Emit an error when constant slice bounds exceed the array length or are reversed. |
| 8530 | fn validateArraySliceBounds 'arena (self: &mut Resolver 'arena, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
| 8531 | let mut startVal: ?u32 = nil; |
| 8532 | let mut endVal: ?u32 = length; |
| 8533 | |
| 8534 | if let startNode = range.start { |
| 8535 | if let val = constSliceIndex(self, startNode) { |
| 8536 | set startVal = val; |
| 8537 | } |
| 8538 | } |
| 8539 | if let endNode = range.end { |
| 8540 | if let val = constSliceIndex(self, endNode) { |
| 8541 | set endVal = val; |
| 8542 | } |
| 8543 | } |
| 8544 | if let val = startVal; val > length { |
| 8545 | throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds); |
| 8546 | } |
| 8547 | if let val = endVal; val > length { |
| 8548 | throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds); |
| 8549 | } |
| 8550 | if let start = startVal { |
| 8551 | if let end = endVal; start > end { |
| 8552 | throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds); |
| 8553 | } |
| 8554 | } |
| 8555 | } |
| 8556 | |
| 8557 | /// Check that an index expression has an unsigned integer type. |
| 8558 | /// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals. |
| 8559 | /// Smaller types are widened to `u32` via a numeric cast coercion. |
| 8560 | unsafe fn checkIndex 'arena (self: &mut Resolver 'arena, indexNode: *ast::Node) throws (ResolveError) { |
| 8561 | let indexTy = try visit(self, indexNode, Type::U32); |
| 8562 | if indexTy == Type::Int or indexTy == Type::U32 { |
| 8563 | let _ = try expectAssignable(self, Type::U32, indexTy, indexNode); |
| 8564 | return; |
| 8565 | } |
| 8566 | match indexTy { |
| 8567 | case Type::U8, Type::U16 => { |
| 8568 | setNodeCoercion(self, indexNode, Coercion::NumericCast { |
| 8569 | from: indexTy, to: Type::U32, |
| 8570 | }); |
| 8571 | } |
| 8572 | else => { |
| 8573 | throw emitTypeMismatch(self, indexNode, TypeMismatch { |
| 8574 | expected: Type::U32, |
| 8575 | actual: indexTy, |
| 8576 | }); |
| 8577 | } |
| 8578 | } |
| 8579 | } |
| 8580 | |
| 8581 | /// Analyze an array or slice subscript expression. |
| 8582 | unsafe fn resolveSubscript 'arena (self: &mut Resolver 'arena, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type |
| 8583 | throws (ResolveError) |
| 8584 | { |
| 8585 | // Range subscripts always require `&` to form a slice. |
| 8586 | if let case ast::NodeValue::Range(range) = indexNode.value { |
| 8587 | let _ = try infer(self, indexNode); |
| 8588 | let _ = try infer(self, container); |
| 8589 | try checkSliceRangeIndices(self, range); |
| 8590 | throw emitError(self, node, ErrorKind::SliceRequiresAddress); |
| 8591 | } |
| 8592 | let containerTy = try infer(self, container); |
| 8593 | if isUnsafePointerType(containerTy) { |
| 8594 | try requireUnsafe(self, container); |
| 8595 | } |
| 8596 | try checkIndex(self, indexNode); |
| 8597 | let subjectTy = autoDeref(containerTy); |
| 8598 | if let case Type::Slice { item, .. } = subjectTy { |
| 8599 | return setNodeType(self, node, *item); |
| 8600 | } |
| 8601 | |
| 8602 | match subjectTy { |
| 8603 | case Type::Array(arrayInfo) => { |
| 8604 | return setNodeType(self, node, *arrayInfo.item); |
| 8605 | } |
| 8606 | else => { |
| 8607 | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
| 8608 | } |
| 8609 | } |
| 8610 | } |
| 8611 | |
| 8612 | /// Find a record field by name. |
| 8613 | fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 { |
| 8614 | for field, i in fields { |
| 8615 | if let name = field.name { |
| 8616 | if name == fieldName { |
| 8617 | return i; |
| 8618 | } |
| 8619 | } |
| 8620 | } |
| 8621 | return nil; |
| 8622 | } |
| 8623 | |
| 8624 | /// Analyze a union constructor call with payload. |
| 8625 | unsafe fn resolveUnionConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type |
| 8626 | throws (ResolveError) |
| 8627 | { |
| 8628 | // Get the union nominal type. |
| 8629 | let case NominalType::Union(unionType) = *unionNominal |
| 8630 | else panic "resolveUnionConstructorCall: not a union type"; |
| 8631 | |
| 8632 | // Callee was already visited; get the variant index it set. |
| 8633 | let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else { |
| 8634 | throw emitError(self, call.callee, ErrorKind::Internal); |
| 8635 | }; |
| 8636 | let variant = &unionType.variants[index]; |
| 8637 | |
| 8638 | // Associate variant index with `call` node for the lowerer. |
| 8639 | setVariantInfo(self, node, index, tag); |
| 8640 | |
| 8641 | // Check if this variant expects a payload. |
| 8642 | let payloadType = variant.valueType; |
| 8643 | if payloadType <> Type::Void { |
| 8644 | try ensureTypeResolved(self, payloadType, node); |
| 8645 | let recInfo = getRecord(payloadType) |
| 8646 | else panic "resolveUnionVariantConstructor: payload is not a record"; |
| 8647 | try checkRecordConstructorArgs(self, node, call.args, recInfo); |
| 8648 | } else { |
| 8649 | if call.args.len > 0 { |
| 8650 | throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
| 8651 | } |
| 8652 | } |
| 8653 | return setNodeType(self, node, Type::Nominal(unionNominal)); |
| 8654 | } |
| 8655 | |
| 8656 | /// Analyze an unlabeled record constructor call. |
| 8657 | /// |
| 8658 | /// Handles the syntax `R(a, b)` for unlabeled records, checking that the |
| 8659 | /// number of arguments matches the record's field count and that each argument |
| 8660 | /// is assignable to its corresponding field type. |
| 8661 | unsafe fn resolveRecordConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type |
| 8662 | throws (ResolveError) |
| 8663 | { |
| 8664 | let case NominalType::Record(recInfo) = *recordType |
| 8665 | else panic "resolveRecordConstructorCall: not a record type"; |
| 8666 | |
| 8667 | try requireRecordAccess(self, node, recInfo); |
| 8668 | try checkRecordConstructorArgs(self, node, call.args, recInfo); |
| 8669 | return setNodeType(self, node, Type::Nominal(recordType)); |
| 8670 | } |
| 8671 | |
| 8672 | /// Resolve the type name of a record literal, handling both record types and |
| 8673 | /// union variant payloads like `Union::Variant { ... }`. |
| 8674 | unsafe fn resolveRecordLitType 'arena ( |
| 8675 | self: &mut Resolver 'arena, node: *ast::Node, typeIdent: *ast::Node, hint: Type |
| 8676 | ) -> ResolvedRecordLitType |
| 8677 | throws (ResolveError) |
| 8678 | { |
| 8679 | if let case ast::NodeValue::RegionApply { .. } = typeIdent.value { |
| 8680 | let ty = try infer(self, typeIdent); |
| 8681 | let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::ExpectedRecord); |
| 8682 | return ResolvedRecordLitType { recordType: info, resultType: ty }; |
| 8683 | } |
| 8684 | // Check if this is a scope access that might be a union variant. |
| 8685 | if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value { |
| 8686 | let scope = self.scope; |
| 8687 | let sym = try resolveAccess(self, typeIdent, access, scope); |
| 8688 | |
| 8689 | // Check if resolved symbol is a union variant. |
| 8690 | if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data { |
| 8691 | let sourceTy = typeFor(self, typeIdent) else panic "resolveRecordLitType: missing union type"; |
| 8692 | let case Type::Nominal(source) = sourceTy else panic "resolveRecordLitType: invalid union type"; |
| 8693 | let unionNominalType = hintedNominal(source, hint); |
| 8694 | try requireNominalArguments(self, unionNominalType, typeIdent); |
| 8695 | try ensureNominalResolved(self, unionNominalType, typeIdent); |
| 8696 | let case NominalType::Union(body) = *unionNominalType else panic; |
| 8697 | let case Type::Nominal(payloadInfo) = body.variants[ordinal].valueType |
| 8698 | else throw emitError(self, node, ErrorKind::ExpectedRecord); |
| 8699 | |
| 8700 | // Store the variant index for the lowerer. |
| 8701 | setVariantInfo(self, node, ordinal, index); |
| 8702 | |
| 8703 | return ResolvedRecordLitType { |
| 8704 | recordType: payloadInfo, |
| 8705 | resultType: Type::Nominal(unionNominalType), |
| 8706 | }; |
| 8707 | } |
| 8708 | // Not a variant, must be a type. |
| 8709 | let case SymbolData::Type(ty) = sym.data |
| 8710 | else throw emitError(self, node, ErrorKind::ExpectedRecord); |
| 8711 | return ResolvedRecordLitType { |
| 8712 | recordType: ty, |
| 8713 | resultType: Type::Nominal(ty), |
| 8714 | }; |
| 8715 | } |
| 8716 | // Simple identifier, resolve as type name. |
| 8717 | let tyInfo = try resolveTypeName(self, typeIdent); |
| 8718 | return ResolvedRecordLitType { |
| 8719 | recordType: tyInfo, |
| 8720 | resultType: Type::Nominal(tyInfo), |
| 8721 | }; |
| 8722 | } |
| 8723 | |
| 8724 | /// Analyze a record literal expression. |
| 8725 | unsafe fn resolveRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
| 8726 | throws (ResolveError) |
| 8727 | { |
| 8728 | // If no type name, infer an anonymous tuple type. |
| 8729 | let typeIdent = lit.typeName else { |
| 8730 | return try resolveAnonRecordLit(self, node, lit, hint); |
| 8731 | }; |
| 8732 | // Resolve the type name, handling both record types and union variants. |
| 8733 | let resolved = try resolveRecordLitType(self, node, typeIdent, hint); |
| 8734 | let mut tyInfo = resolved.recordType; |
| 8735 | let mut resultType = resolved.resultType; |
| 8736 | let mut target = hint; |
| 8737 | if let case Type::Optional(inner) = target { |
| 8738 | set target = *inner; |
| 8739 | } |
| 8740 | if nominalApplication(tyInfo) == nil { |
| 8741 | if let case Type::Nominal(info) = target { |
| 8742 | if let applied = nominalApplication(info); applied.base == tyInfo { |
| 8743 | set tyInfo = info; |
| 8744 | set resultType = target; |
| 8745 | } |
| 8746 | } |
| 8747 | } |
| 8748 | try requireNominalArguments(self, tyInfo, typeIdent); |
| 8749 | |
| 8750 | // Lazily resolve record body if not yet done. |
| 8751 | try ensureNominalResolved(self, tyInfo, typeIdent); |
| 8752 | let case NominalType::Record(recordType) = *tyInfo |
| 8753 | else throw emitError(self, node, ErrorKind::ExpectedRecord); |
| 8754 | |
| 8755 | try requireRecordAccess(self, node, recordType); |
| 8756 | // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax. |
| 8757 | if not recordType.labeled { |
| 8758 | throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch); |
| 8759 | } |
| 8760 | // Check field count. With `{ .. }` syntax, fewer fields are allowed. |
| 8761 | if lit.fields.len > recordType.fields.len { |
| 8762 | throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
| 8763 | expected: recordType.fields.len as u32, |
| 8764 | actual: lit.fields.len, |
| 8765 | })); |
| 8766 | } |
| 8767 | if not lit.ignoreRest and lit.fields.len < recordType.fields.len { |
| 8768 | let missingName = recordType.fields[lit.fields.len].name else panic; |
| 8769 | throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName)); |
| 8770 | } |
| 8771 | |
| 8772 | // Fields must be in declaration order. |
| 8773 | for fieldNode, idx in lit.fields { |
| 8774 | let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value |
| 8775 | else panic "resolveRecordLit: expected field node value"; |
| 8776 | let label = fieldArg.label |
| 8777 | else panic "resolveRecordLit: expected labeled field"; |
| 8778 | let fieldName = try nodeName(self, label); |
| 8779 | let expected = recordType.fields[idx]; |
| 8780 | let expectedName = expected.name else panic; |
| 8781 | |
| 8782 | if fieldName <> expectedName { |
| 8783 | throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder { |
| 8784 | field: fieldName, |
| 8785 | prev: expectedName, |
| 8786 | }); |
| 8787 | } |
| 8788 | setRecordFieldIndex(self, fieldNode, idx); |
| 8789 | try checkAssignable(self, fieldArg.value, expected.fieldType); |
| 8790 | setNodeType(self, fieldNode, expected.fieldType); |
| 8791 | } |
| 8792 | return setNodeType(self, node, resultType); |
| 8793 | } |
| 8794 | |
| 8795 | /// Analyze an anonymous record literal, checking fields against the hint type. |
| 8796 | unsafe fn resolveAnonRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
| 8797 | throws (ResolveError) |
| 8798 | { |
| 8799 | // Unwrap optional hint to get the inner record type. |
| 8800 | let mut innerHint = hint; |
| 8801 | if let case Type::Optional(inner) = hint { |
| 8802 | set innerHint = *inner; |
| 8803 | } |
| 8804 | let mut hintInfo: ?RecordType = nil; |
| 8805 | if let case Type::Nominal(info) = innerHint { |
| 8806 | try ensureNominalResolved(self, info, node); |
| 8807 | if let case NominalType::Record(s) = *info { |
| 8808 | set hintInfo = s; |
| 8809 | } |
| 8810 | } |
| 8811 | let targetInfo = hintInfo else { |
| 8812 | throw emitError(self, node, ErrorKind::CannotInferType); |
| 8813 | }; |
| 8814 | |
| 8815 | try requireRecordAccess(self, node, targetInfo); |
| 8816 | // Check field count. |
| 8817 | if lit.fields.len <> targetInfo.fields.len { |
| 8818 | if lit.fields.len < targetInfo.fields.len { |
| 8819 | let missingName = targetInfo.fields[lit.fields.len].name else panic; |
| 8820 | throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName)); |
| 8821 | } else { |
| 8822 | throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
| 8823 | expected: targetInfo.fields.len as u32, |
| 8824 | actual: lit.fields.len, |
| 8825 | })); |
| 8826 | } |
| 8827 | } |
| 8828 | |
| 8829 | // Fields must be in declaration order. |
| 8830 | for fieldNode, idx in lit.fields { |
| 8831 | let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value |
| 8832 | else panic "resolveAnonRecordLit: expected field node value"; |
| 8833 | let label = fieldArg.label |
| 8834 | else panic "resolveAnonRecordLit: expected labeled field"; |
| 8835 | let fieldName = try nodeName(self, label); |
| 8836 | let expected = targetInfo.fields[idx]; |
| 8837 | let expectedName = expected.name else panic; |
| 8838 | |
| 8839 | if fieldName <> expectedName { |
| 8840 | throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder { |
| 8841 | field: fieldName, |
| 8842 | prev: expectedName, |
| 8843 | }); |
| 8844 | } |
| 8845 | setRecordFieldIndex(self, fieldNode, idx); |
| 8846 | let fieldType = try visit(self, fieldArg.value, expected.fieldType); |
| 8847 | |
| 8848 | try expectAssignable(self, expected.fieldType, fieldType, fieldArg.value); |
| 8849 | setNodeType(self, fieldNode, fieldType); |
| 8850 | } |
| 8851 | return setNodeType(self, node, innerHint); |
| 8852 | } |
| 8853 | |
| 8854 | /// Analyze an array literal expression. |
| 8855 | unsafe fn resolveArrayLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type |
| 8856 | throws (ResolveError) |
| 8857 | { |
| 8858 | let length = items.len; |
| 8859 | let mut expectedTy: Type = Type::Unknown; |
| 8860 | |
| 8861 | if let case Type::Array(ary) = hint { |
| 8862 | set expectedTy = *ary.item; |
| 8863 | } else if let case Type::Optional(inner) = hint { |
| 8864 | if let case Type::Array(ary) = *inner { |
| 8865 | set expectedTy = *ary.item; |
| 8866 | } |
| 8867 | }; |
| 8868 | for itemNode in items { |
| 8869 | let itemTy = try visit(self, itemNode, expectedTy); |
| 8870 | assert itemTy <> Type::Unknown; |
| 8871 | |
| 8872 | // Set the expected type to the first type we encounter. |
| 8873 | if expectedTy == Type::Unknown { |
| 8874 | set expectedTy = itemTy; |
| 8875 | } else { |
| 8876 | try expectAssignable(self, expectedTy, itemTy, itemNode); |
| 8877 | } |
| 8878 | } |
| 8879 | if expectedTy == Type::Unknown { |
| 8880 | throw emitError(self, node, ErrorKind::CannotInferType); |
| 8881 | }; |
| 8882 | let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length }); |
| 8883 | return setNodeType(self, node, arrayTy); |
| 8884 | } |
| 8885 | |
| 8886 | /// Analyze an array repeat literal expression. |
| 8887 | unsafe fn resolveArrayRepeat 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
| 8888 | throws (ResolveError) |
| 8889 | { |
| 8890 | let mut itemHint = hint; |
| 8891 | if let case Type::Array(ary) = hint { |
| 8892 | set itemHint = *ary.item; |
| 8893 | } else if let case Type::Optional(inner) = hint { |
| 8894 | if let case Type::Array(ary) = *inner { |
| 8895 | set itemHint = *ary.item; |
| 8896 | } |
| 8897 | } |
| 8898 | let valueTy = try visit(self, lit.item, itemHint); |
| 8899 | let count = try checkSizeInt(self, lit.count); |
| 8900 | let arrayTy = Type::Array(ArrayType { |
| 8901 | item: allocType(self, valueTy), |
| 8902 | length: count, |
| 8903 | }); |
| 8904 | return setNodeType(self, node, arrayTy); |
| 8905 | } |
| 8906 | |
| 8907 | /// Resolve union variant access. |
| 8908 | unsafe fn resolveUnionVariantAccess 'arena ( |
| 8909 | self: &mut Resolver 'arena, |
| 8910 | node: *ast::Node, |
| 8911 | access: ast::Access, |
| 8912 | unionType: UnionType, |
| 8913 | variantName: *[u8] |
| 8914 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 8915 | // Look up the variant in the union's nominal type. |
| 8916 | for i in 0..unionType.variants.len { |
| 8917 | let variant = &unionType.variants[i]; |
| 8918 | if variant.name == variantName { |
| 8919 | let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data |
| 8920 | else panic "resolveUnionVariantAccess: expected variant symbol"; |
| 8921 | |
| 8922 | // Associate the variant symbol with the child node. |
| 8923 | setNodeSymbol(self, access.child, variant.symbol); |
| 8924 | setNodeSymbol(self, node, variant.symbol); |
| 8925 | |
| 8926 | // Store the variant index for the lowerer. |
| 8927 | setVariantInfo(self, node, ordinal, index); |
| 8928 | |
| 8929 | return variant.symbol; |
| 8930 | } |
| 8931 | } |
| 8932 | throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName)); |
| 8933 | } |
| 8934 | |
| 8935 | /// Analyze a scope access expression. |
| 8936 | unsafe fn resolveScopeAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access, hint: Type) -> Type |
| 8937 | throws (ResolveError) |
| 8938 | { |
| 8939 | let scope = self.scope; |
| 8940 | let sym = try resolveAccess(self, node, access, scope); |
| 8941 | try checkStaticAccess(self, node, sym); |
| 8942 | let mut ty: Type = undefined; |
| 8943 | |
| 8944 | match sym.data { |
| 8945 | case SymbolData::Value { type, .. } => { |
| 8946 | setNodeSymbol(self, node, sym); |
| 8947 | set ty = type; |
| 8948 | } |
| 8949 | case SymbolData::Constant { type, value } => { |
| 8950 | // Propagate the constant value. |
| 8951 | if let val = value { |
| 8952 | setNodeConstValue(self, node, val); |
| 8953 | } |
| 8954 | setNodeSymbol(self, node, sym); |
| 8955 | set ty = type; |
| 8956 | } |
| 8957 | case SymbolData::Type(t) => { |
| 8958 | setNodeSymbol(self, node, sym); |
| 8959 | set ty = Type::Nominal(hintedNominal(t, hint)); |
| 8960 | } |
| 8961 | case SymbolData::Variant { index, .. } => { |
| 8962 | let ty = typeFor(self, node) |
| 8963 | else throw emitError(self, node, ErrorKind::Internal); |
| 8964 | let case Type::Nominal(info) = ty else panic "resolveScopeAccess: invalid variant type"; |
| 8965 | let applied = hintedNominal(info, hint); |
| 8966 | try requireNominalArguments(self, applied, node); |
| 8967 | try ensureNominalResolved(self, applied, node); |
| 8968 | let variantTy = Type::Nominal(applied); |
| 8969 | // For unions without payload, store the variant index as a constant. |
| 8970 | if isVoidUnion(variantTy) { |
| 8971 | setNodeConstValue(self, node, ConstValue::Int(ConstInt { |
| 8972 | magnitude: index as u64, |
| 8973 | bits: 32, |
| 8974 | signed: false, |
| 8975 | negative: false, |
| 8976 | })); |
| 8977 | } |
| 8978 | return setNodeType(self, node, variantTy); |
| 8979 | } |
| 8980 | case SymbolData::Module { .. } => { |
| 8981 | throw emitError(self, node, ErrorKind::UnexpectedModuleName); |
| 8982 | } |
| 8983 | case SymbolData::Trait(_) => { // Trait names are not values. |
| 8984 | throw emitError(self, node, ErrorKind::UnexpectedTraitName); |
| 8985 | } |
| 8986 | } |
| 8987 | return setNodeType(self, node, ty); |
| 8988 | } |
| 8989 | |
| 8990 | /// Analyze a field access expression. |
| 8991 | unsafe fn resolveFieldAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access) -> Type |
| 8992 | throws (ResolveError) |
| 8993 | { |
| 8994 | let parentTy = try infer(self, access.parent); |
| 8995 | if isUnsafePointerType(parentTy) { |
| 8996 | try requireUnsafe(self, access.parent); |
| 8997 | } |
| 8998 | let subjectTy = autoDeref(parentTy); |
| 8999 | try ensureTypeResolved(self, subjectTy, access.parent); |
| 9000 | |
| 9001 | if let case Type::Slice { class, item, mutable } = subjectTy { |
| 9002 | let fieldNode = access.child; |
| 9003 | let fieldName = try nodeName(self, fieldNode); |
| 9004 | if mem::eq(fieldName, PTR_FIELD) { |
| 9005 | try requireUnsafe(self, node); |
| 9006 | setRecordFieldIndex(self, fieldNode, 0); |
| 9007 | return setNodeType( |
| 9008 | self, |
| 9009 | node, |
| 9010 | Type::Pointer { class, target: item, mutable }, |
| 9011 | ); |
| 9012 | } |
| 9013 | if mem::eq(fieldName, LEN_FIELD) { |
| 9014 | setRecordFieldIndex(self, fieldNode, 1); |
| 9015 | return setNodeType(self, node, Type::U32); |
| 9016 | } |
| 9017 | if mem::eq(fieldName, CAP_FIELD) { |
| 9018 | setRecordFieldIndex(self, fieldNode, 2); |
| 9019 | return setNodeType(self, node, Type::U32); |
| 9020 | } |
| 9021 | throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName)); |
| 9022 | } |
| 9023 | if let case Type::TraitObject { traitInfo, .. } = subjectTy { |
| 9024 | let fieldName = try nodeName(self, access.child); |
| 9025 | let method = findTraitMethod(&traitInfo.methods[..], fieldName) |
| 9026 | else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName)); |
| 9027 | return setNodeType(self, node, Type::Fn(method.fnType)); |
| 9028 | } |
| 9029 | |
| 9030 | match subjectTy { |
| 9031 | case Type::Nominal(NominalType::Record(recordType)) => { |
| 9032 | let fieldNode = access.child; |
| 9033 | let fieldName = try nodeName(self, fieldNode); |
| 9034 | if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) { |
| 9035 | try requireRecordAccess(self, node, recordType); |
| 9036 | let fieldTy = recordType.fields[fieldIndex].fieldType; |
| 9037 | setRecordFieldIndex(self, fieldNode, fieldIndex); |
| 9038 | return setNodeType(self, node, fieldTy); |
| 9039 | } |
| 9040 | // Not a field: check for a standalone method. |
| 9041 | if let method = findMethod(self, subjectTy, fieldName) { |
| 9042 | return setNodeType(self, node, Type::Fn(method.fnType)); |
| 9043 | } |
| 9044 | throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName)); |
| 9045 | } |
| 9046 | case Type::Array(arrayInfo) => { |
| 9047 | let fieldNode = access.child; |
| 9048 | let fieldName = try nodeName(self, fieldNode); |
| 9049 | |
| 9050 | if mem::eq(fieldName, LEN_FIELD) { |
| 9051 | let lengthConst = constInt(arrayInfo.length as u64, 32, false, false); |
| 9052 | setNodeConstValue(self, node, lengthConst); |
| 9053 | |
| 9054 | return setNodeType(self, node, Type::U32); |
| 9055 | } |
| 9056 | throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName)); |
| 9057 | } |
| 9058 | |
| 9059 | else => { |
| 9060 | // Check for standalone methods on any nominal type (e.g. unions). |
| 9061 | if let case Type::Nominal(_) = subjectTy { |
| 9062 | let fieldName = try nodeName(self, access.child); |
| 9063 | if let method = findMethod(self, subjectTy, fieldName) { |
| 9064 | return setNodeType(self, node, Type::Fn(method.fnType)); |
| 9065 | } |
| 9066 | } |
| 9067 | throw emitError(self, access.parent, ErrorKind::ExpectedRecord); |
| 9068 | } |
| 9069 | } |
| 9070 | } |
| 9071 | |
| 9072 | /// Return whether a pointer-like value grants mutable access. |
| 9073 | fn isMutablePointerLike(ty: Type) -> bool { |
| 9074 | match ty { |
| 9075 | case Type::Pointer { mutable, .. } => return mutable, |
| 9076 | case Type::Slice { mutable, .. } => return mutable, |
| 9077 | case Type::TraitObject { mutable, .. } => return mutable, |
| 9078 | else => return false, |
| 9079 | } |
| 9080 | } |
| 9081 | |
| 9082 | /// Check exclusive access to an element or field of a container. |
| 9083 | unsafe fn canAccessExclusiveProjection 'arena (self: &mut Resolver 'arena, container: *ast::Node) -> bool |
| 9084 | throws (ResolveError) |
| 9085 | { |
| 9086 | let ty = try infer(self, container); |
| 9087 | if let case Type::Slice { mutable: false, .. } = autoDeref(ty) { |
| 9088 | return false; |
| 9089 | } |
| 9090 | match ty { |
| 9091 | case Type::Pointer { class, mutable, .. } => { |
| 9092 | if not mutable { |
| 9093 | return false; |
| 9094 | } |
| 9095 | if class == types::PointerClass::Unsafe { |
| 9096 | return true; |
| 9097 | } |
| 9098 | } |
| 9099 | case Type::Slice { class, mutable, .. } => { |
| 9100 | if not mutable { |
| 9101 | return false; |
| 9102 | } |
| 9103 | if class == types::PointerClass::Unsafe { |
| 9104 | return true; |
| 9105 | } |
| 9106 | } |
| 9107 | else => { |
| 9108 | }, |
| 9109 | } |
| 9110 | return try canAccessExclusiveHandle(self, container); |
| 9111 | } |
| 9112 | |
| 9113 | /// Check that a stored exclusive handle is not reached through shared access. |
| 9114 | unsafe fn canAccessExclusiveHandle 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
| 9115 | throws (ResolveError) |
| 9116 | { |
| 9117 | match node.value { |
| 9118 | case ast::NodeValue::FieldAccess(access) => |
| 9119 | return try canAccessExclusiveProjection(self, access.parent), |
| 9120 | case ast::NodeValue::Subscript { container, .. } => |
| 9121 | return try canAccessExclusiveProjection(self, container), |
| 9122 | case ast::NodeValue::Deref(inner) => |
| 9123 | return try canAccessExclusiveProjection(self, inner), |
| 9124 | case ast::NodeValue::As(expr) => |
| 9125 | return try canAccessExclusiveHandle(self, expr.value), |
| 9126 | case ast::NodeValue::CondExpr(cond) => { |
| 9127 | if not try canAccessExclusiveHandle(self, cond.thenExpr) { |
| 9128 | return false; |
| 9129 | } |
| 9130 | return try canAccessExclusiveHandle(self, cond.elseExpr); |
| 9131 | } |
| 9132 | else => return true, |
| 9133 | } |
| 9134 | } |
| 9135 | |
| 9136 | /// Check target mutability for implicit pointer access. |
| 9137 | unsafe fn canMutateThrough 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
| 9138 | throws (ResolveError) |
| 9139 | { |
| 9140 | let ty = try infer(self, node); |
| 9141 | match ty { |
| 9142 | case Type::Pointer { class, mutable, .. } => { |
| 9143 | if not mutable { |
| 9144 | return false; |
| 9145 | } |
| 9146 | if class == types::PointerClass::Unsafe { |
| 9147 | return true; |
| 9148 | } |
| 9149 | return try canAccessExclusiveHandle(self, node); |
| 9150 | } |
| 9151 | case Type::Slice { class, mutable, .. } => { |
| 9152 | if not mutable { |
| 9153 | return false; |
| 9154 | } |
| 9155 | if class == types::PointerClass::Unsafe { |
| 9156 | return true; |
| 9157 | } |
| 9158 | return try canAccessExclusiveHandle(self, node); |
| 9159 | } |
| 9160 | case Type::TraitObject { class, mutable, .. } => { |
| 9161 | if not mutable { |
| 9162 | return false; |
| 9163 | } |
| 9164 | if class == types::PointerClass::Unsafe { |
| 9165 | return true; |
| 9166 | } |
| 9167 | return try canAccessExclusiveHandle(self, node); |
| 9168 | } |
| 9169 | else => return try canBorrowMutFrom(self, node), |
| 9170 | } |
| 9171 | } |
| 9172 | |
| 9173 | /// Determine whether an expression can yield a mutable location for borrowing. |
| 9174 | unsafe fn canBorrowMutFrom 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
| 9175 | throws (ResolveError) |
| 9176 | { |
| 9177 | match node.value { |
| 9178 | case ast::NodeValue::Ident(name) => { |
| 9179 | let sym = findValueSymbol(self.scope, name) |
| 9180 | else return false; |
| 9181 | let case SymbolData::Value { mutable, .. } = sym.data |
| 9182 | else return false; |
| 9183 | return mutable; |
| 9184 | } |
| 9185 | case ast::NodeValue::FieldAccess(access) => { |
| 9186 | let parentTy = try infer(self, access.parent); |
| 9187 | if let case Type::Slice { .. } = autoDeref(parentTy) { |
| 9188 | try requireUnsafe(self, node); |
| 9189 | } |
| 9190 | return try canMutateThrough(self, access.parent); |
| 9191 | } |
| 9192 | case ast::NodeValue::ScopeAccess(_) => { |
| 9193 | // Module-qualified access to a top-level symbol. A `static` |
| 9194 | // binds as a mutable value; a `constant` does not. |
| 9195 | let _ = try infer(self, node); |
| 9196 | let sym = symbolFor(self, node) |
| 9197 | else return false; |
| 9198 | |
| 9199 | if let case SymbolData::Value { mutable, .. } = sym.data { |
| 9200 | return mutable; |
| 9201 | } |
| 9202 | return false; |
| 9203 | } |
| 9204 | case ast::NodeValue::Subscript { container, .. } => { |
| 9205 | let containerTy = try infer(self, container); |
| 9206 | // Subscript auto-derefs pointers, so check the actual indexed type. |
| 9207 | let subjectTy = autoDeref(containerTy); |
| 9208 | |
| 9209 | if let case Type::Slice { mutable, .. } = subjectTy { |
| 9210 | if not mutable { |
| 9211 | return false; |
| 9212 | } |
| 9213 | return try canMutateThrough(self, container); |
| 9214 | } |
| 9215 | if let case Type::Array(_) = subjectTy { |
| 9216 | return try canMutateThrough(self, container); |
| 9217 | } |
| 9218 | return false; |
| 9219 | } |
| 9220 | case ast::NodeValue::ArrayLit(_), |
| 9221 | ast::NodeValue::ArrayRepeatLit(_) => |
| 9222 | { |
| 9223 | return true; |
| 9224 | } |
| 9225 | case ast::NodeValue::Call(_) => { |
| 9226 | // A call returning `*mut T` (or `&mut [T]`) yields a |
| 9227 | // mutable place. Non-pointer returns cannot be mutably borrowed. |
| 9228 | let ty = try infer(self, node); |
| 9229 | if let case Type::Pointer { mutable, .. } = ty { |
| 9230 | return mutable; |
| 9231 | } |
| 9232 | if let case Type::Slice { mutable, .. } = ty { |
| 9233 | return mutable; |
| 9234 | } |
| 9235 | return false; |
| 9236 | } |
| 9237 | case ast::NodeValue::Deref(inner) => { |
| 9238 | let innerTy = try infer(self, inner); |
| 9239 | |
| 9240 | if let case Type::Pointer { .. } = innerTy { |
| 9241 | return try canMutateThrough(self, inner); |
| 9242 | } |
| 9243 | if let case Type::Slice { .. } = innerTy { |
| 9244 | return try canMutateThrough(self, inner); |
| 9245 | } |
| 9246 | // Record deref: mutability depends on the inner binding. |
| 9247 | if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy { |
| 9248 | if not recInfo.labeled and recInfo.fields.len == 1 { |
| 9249 | return try canBorrowMutFrom(self, inner); |
| 9250 | } |
| 9251 | } |
| 9252 | return false; |
| 9253 | } |
| 9254 | else => { |
| 9255 | return false; |
| 9256 | } |
| 9257 | } |
| 9258 | } |
| 9259 | |
| 9260 | /// Restrict a pointee lifetime to the borrow of its exclusive owner. |
| 9261 | unsafe fn constrainAddressClass(storage: types::PointerClass, owner: types::PointerClass) -> types::PointerClass { |
| 9262 | if owner == types::PointerClass::Owned or owner == types::PointerClass::Unsafe { |
| 9263 | return storage; |
| 9264 | } |
| 9265 | if owner == types::PointerClass::Ref { |
| 9266 | return owner; |
| 9267 | } |
| 9268 | let case types::PointerClass::Region(ownerRegion) = owner else panic; |
| 9269 | if let case types::PointerClass::Region(storageRegion) = storage { |
| 9270 | if types::regionContains(storageRegion, ownerRegion) { |
| 9271 | return owner; |
| 9272 | } |
| 9273 | if types::regionContains(ownerRegion, storageRegion) { |
| 9274 | return storage; |
| 9275 | } |
| 9276 | return types::PointerClass::Ref; |
| 9277 | } |
| 9278 | if storage == types::PointerClass::Owned { |
| 9279 | return owner; |
| 9280 | } |
| 9281 | return storage; |
| 9282 | } |
| 9283 | |
| 9284 | /// Get the usable pointee lifetime of a pointer or slice value. |
| 9285 | unsafe fn pointerAddressClass 'arena ( |
| 9286 | self: &Resolver 'arena, node: *ast::Node, class: types::PointerClass, mutable: bool |
| 9287 | ) -> types::PointerClass { |
| 9288 | if not mutable or class == types::PointerClass::Unsafe { |
| 9289 | return class; |
| 9290 | } |
| 9291 | return constrainAddressClass(class, exclusiveOwnerClass(self, node)); |
| 9292 | } |
| 9293 | |
| 9294 | /// Get the lifetime of storage selected by a pointer or slice subscript. |
| 9295 | unsafe fn indexedPointerClass 'arena (self: &Resolver 'arena, container: *ast::Node) -> ?types::PointerClass { |
| 9296 | let ty = typeFor(self, container) else return nil; |
| 9297 | if let case Type::Pointer { class, target, mutable } = ty { |
| 9298 | let parentClass = pointerAddressClass(self, container, class, mutable); |
| 9299 | if let case Type::Slice { class: sliceClass, mutable: sliceMutable, .. } = *target { |
| 9300 | if not sliceMutable or sliceClass == types::PointerClass::Unsafe { |
| 9301 | return sliceClass; |
| 9302 | } |
| 9303 | return constrainAddressClass(sliceClass, parentClass); |
| 9304 | } |
| 9305 | return parentClass; |
| 9306 | } |
| 9307 | if let case Type::Slice { class, mutable, .. } = ty { |
| 9308 | return pointerAddressClass(self, container, class, mutable); |
| 9309 | } |
| 9310 | return nil; |
| 9311 | } |
| 9312 | |
| 9313 | /// Get the borrow that controls access to a stored exclusive handle. |
| 9314 | /// Directly owned values have no additional borrow restriction. |
| 9315 | unsafe fn exclusiveOwnerClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass { |
| 9316 | match node.value { |
| 9317 | case ast::NodeValue::FieldAccess(access) => { |
| 9318 | if let ty = typeFor(self, access.parent) { |
| 9319 | match ty { |
| 9320 | case Type::Pointer { class, mutable, .. } => |
| 9321 | return pointerAddressClass(self, access.parent, class, mutable), |
| 9322 | case Type::Slice { class, mutable, .. } => |
| 9323 | return pointerAddressClass(self, access.parent, class, mutable), |
| 9324 | else => { |
| 9325 | }, |
| 9326 | } |
| 9327 | } |
| 9328 | return exclusiveOwnerClass(self, access.parent); |
| 9329 | } |
| 9330 | case ast::NodeValue::Subscript { container, .. } => { |
| 9331 | if let class = indexedPointerClass(self, container) { |
| 9332 | return class; |
| 9333 | } |
| 9334 | return exclusiveOwnerClass(self, container); |
| 9335 | } |
| 9336 | case ast::NodeValue::Deref(target) => { |
| 9337 | if let ty = typeFor(self, target) { |
| 9338 | if let case Type::Pointer { class, mutable, .. } = ty { |
| 9339 | return pointerAddressClass(self, target, class, mutable); |
| 9340 | } |
| 9341 | } |
| 9342 | return exclusiveOwnerClass(self, target); |
| 9343 | } |
| 9344 | case ast::NodeValue::As(expr) => return exclusiveOwnerClass(self, expr.value), |
| 9345 | case ast::NodeValue::CondExpr(cond) => |
| 9346 | return constrainAddressClass(exclusiveOwnerClass(self, cond.thenExpr), exclusiveOwnerClass(self, cond.elseExpr)), |
| 9347 | else => return types::PointerClass::Owned, |
| 9348 | } |
| 9349 | } |
| 9350 | |
| 9351 | /// Return the storage class of an addressed location. |
| 9352 | unsafe fn addressStorageClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass { |
| 9353 | match node.value { |
| 9354 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
| 9355 | if let sym = symbolFor(self, node) { |
| 9356 | match sym.node.value { |
| 9357 | case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) => |
| 9358 | return types::PointerClass::Owned, |
| 9359 | else => {} |
| 9360 | } |
| 9361 | } |
| 9362 | } |
| 9363 | case ast::NodeValue::FieldAccess(access) => { |
| 9364 | if let ty = typeFor(self, access.parent) { |
| 9365 | if let case Type::Pointer { class, mutable, .. } = ty { |
| 9366 | return pointerAddressClass(self, access.parent, class, mutable); |
| 9367 | } |
| 9368 | } |
| 9369 | return addressStorageClass(self, access.parent); |
| 9370 | } |
| 9371 | case ast::NodeValue::Subscript { container, .. } => { |
| 9372 | if let class = indexedPointerClass(self, container) { |
| 9373 | return class; |
| 9374 | } |
| 9375 | return addressStorageClass(self, container); |
| 9376 | } |
| 9377 | case ast::NodeValue::Deref(target) => { |
| 9378 | if let ty = typeFor(self, target) { |
| 9379 | if let case Type::Pointer { class, mutable, .. } = ty { |
| 9380 | return pointerAddressClass(self, target, class, mutable); |
| 9381 | } |
| 9382 | } |
| 9383 | return addressStorageClass(self, target); |
| 9384 | } |
| 9385 | else => {} |
| 9386 | } |
| 9387 | return types::PointerClass::Ref; |
| 9388 | } |
| 9389 | |
| 9390 | /// Select an address type without extending the target storage lifetime. |
| 9391 | unsafe fn addressClass 'arena (self: &mut Resolver 'arena, target: *ast::Node, hint: Type) -> types::PointerClass |
| 9392 | throws (ResolveError) |
| 9393 | { |
| 9394 | if isUnsafePointerType(hint) { |
| 9395 | try requireUnsafe(self, target); |
| 9396 | return types::PointerClass::Unsafe; |
| 9397 | } |
| 9398 | if isRefType(hint) { |
| 9399 | if referenceRegion(hint) <> nil { |
| 9400 | return addressStorageClass(self, target); |
| 9401 | } |
| 9402 | return types::PointerClass::Ref; |
| 9403 | } |
| 9404 | match target.value { |
| 9405 | case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => { |
| 9406 | if isConstExpr(self, target) { |
| 9407 | return types::PointerClass::Owned; |
| 9408 | } |
| 9409 | } |
| 9410 | else => {} |
| 9411 | } |
| 9412 | return addressStorageClass(self, target); |
| 9413 | } |
| 9414 | |
| 9415 | /// Return the cell type whose payload contains a place during type analysis. |
| 9416 | unsafe fn inferCellPayload 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?Type |
| 9417 | throws (ResolveError) |
| 9418 | { |
| 9419 | match node.value { |
| 9420 | case ast::NodeValue::Deref(target) => { |
| 9421 | let ty = try infer(self, target); |
| 9422 | if let case Type::Cell { .. } = ty { |
| 9423 | return ty; |
| 9424 | } |
| 9425 | } |
| 9426 | case ast::NodeValue::FieldAccess(access) => |
| 9427 | return try inferCellPayload(self, access.parent), |
| 9428 | case ast::NodeValue::Subscript { container, .. } => |
| 9429 | return try inferCellPayload(self, container), |
| 9430 | else => {} |
| 9431 | } |
| 9432 | return nil; |
| 9433 | } |
| 9434 | |
| 9435 | /// Return whether a typed expression accesses a whole cell payload. |
| 9436 | export fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
| 9437 | if let case ast::NodeValue::Deref(target) = node.value { |
| 9438 | if let ty = typeFor(self, target) { |
| 9439 | if let case Type::Cell { .. } = ty { |
| 9440 | return true; |
| 9441 | } |
| 9442 | } |
| 9443 | } |
| 9444 | return false; |
| 9445 | } |
| 9446 | |
| 9447 | /// Return whether an expression creates shared mutable access to a source place. |
| 9448 | fn createsCellBorrow(node: *ast::Node) -> bool { |
| 9449 | match node.value { |
| 9450 | case ast::NodeValue::AddressOf(address) => return address.kind == ast::AddressKind::Cell, |
| 9451 | case ast::NodeValue::As(expr) => return createsCellBorrow(expr.value), |
| 9452 | case ast::NodeValue::RegionApply { value, .. } => return createsCellBorrow(value), |
| 9453 | case ast::NodeValue::CondExpr(cond) => |
| 9454 | return createsCellBorrow(cond.thenExpr) or createsCellBorrow(cond.elseExpr), |
| 9455 | else => return false, |
| 9456 | } |
| 9457 | } |
| 9458 | |
| 9459 | /// Find the exclusive handle that owns an addressed place. |
| 9460 | fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node { |
| 9461 | let mut parent: ?*ast::Node = nil; |
| 9462 | match node.value { |
| 9463 | case ast::NodeValue::Deref(target) => set parent = target, |
| 9464 | case ast::NodeValue::FieldAccess(access) => set parent = access.parent, |
| 9465 | case ast::NodeValue::Subscript { container, .. } => set parent = container, |
| 9466 | else => {} |
| 9467 | } |
| 9468 | let parentNode = parent else return nil; |
| 9469 | if let ty = typeFor(self, parentNode) { |
| 9470 | match ty { |
| 9471 | case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parentNode, |
| 9472 | else => {} |
| 9473 | } |
| 9474 | } |
| 9475 | return addressOwner(self, parentNode); |
| 9476 | } |
| 9477 | |
| 9478 | /// Analyze an address-of expression. |
| 9479 | unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
| 9480 | throws (ResolveError) |
| 9481 | { |
| 9482 | let payloadCell = try inferCellPayload(self, addr.target); |
| 9483 | if let cellTy = payloadCell { |
| 9484 | let case Type::Cell { permission, .. } = cellTy |
| 9485 | else panic "resolveAddressOf: invalid cell payload"; |
| 9486 | if permission == nil or addr.kind == ast::AddressKind::Cell { |
| 9487 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
| 9488 | } |
| 9489 | } |
| 9490 | if addr.kind == ast::AddressKind::Cell and not ast::isPlaceExpr(addr.target) { |
| 9491 | throw emitError(self, addr.target, ErrorKind::RefBinding); |
| 9492 | } |
| 9493 | if payloadCell == nil and ast::isExclusiveAddress(addr) { |
| 9494 | if not try canBorrowMutFrom(self, addr.target) { |
| 9495 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
| 9496 | } |
| 9497 | } |
| 9498 | if let case ast::NodeValue::Subscript { container, index } = addr.target.value { |
| 9499 | if let case ast::NodeValue::Range(range) = index.value { |
| 9500 | if addr.kind == ast::AddressKind::Cell { |
| 9501 | throw emitError(self, addr.target, ErrorKind::InvalidCellPayload); |
| 9502 | } |
| 9503 | let containerTy = try infer(self, container); |
| 9504 | let subjectTy = autoDeref(containerTy); |
| 9505 | |
| 9506 | try checkSliceRangeIndices(self, range); |
| 9507 | |
| 9508 | let mut info = sliceRangeInfo(subjectTy) |
| 9509 | else throw emitError(self, container, ErrorKind::ExpectedIndexable); |
| 9510 | if ast::isExclusiveAddress(addr) and not info.mutable { |
| 9511 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
| 9512 | } |
| 9513 | if let capacity = info.capacity { |
| 9514 | try validateArraySliceBounds(self, range, capacity, node); |
| 9515 | } |
| 9516 | set info.mutable = addr.kind == ast::AddressKind::Mutable; |
| 9517 | let class = try addressClass(self, addr.target, hint); |
| 9518 | let sliceTy = Type::Slice { class, item: info.itemType, mutable: info.mutable }; |
| 9519 | let alloc = allocType(self, sliceTy); |
| 9520 | setSliceRangeInfo(self, node, info); |
| 9521 | setNodeType(self, addr.target, *alloc); |
| 9522 | return setNodeType(self, node, *alloc); |
| 9523 | } |
| 9524 | } |
| 9525 | // Derive a hint for the target type from the slice hint. |
| 9526 | let mut targetHint: Type = Type::Unknown; |
| 9527 | if let case Type::Slice { item, .. } = hint { |
| 9528 | set targetHint = Type::Array(ArrayType { item, length: 0 }); |
| 9529 | } |
| 9530 | let targetTy = try visit(self, addr.target, targetHint); |
| 9531 | let class = try addressClass(self, addr.target, hint); |
| 9532 | |
| 9533 | // Mark local variable symbols as address-taken so the lowerer |
| 9534 | // allocates a stack slot eagerly. |
| 9535 | if let case ast::NodeValue::Ident(name) = addr.target.value { |
| 9536 | if let sym = findValueSymbol(self.scope, name) { |
| 9537 | match &mut sym.data { |
| 9538 | case SymbolData::Value { addressTaken, .. } => { |
| 9539 | set *addressTaken = true; |
| 9540 | } |
| 9541 | else => {} |
| 9542 | } |
| 9543 | } |
| 9544 | } |
| 9545 | |
| 9546 | if addr.kind == ast::AddressKind::Cell { |
| 9547 | let mut permission: ?*unsafe types::Region = nil; |
| 9548 | if let permissionNode = addr.permission { |
| 9549 | set permission = try resolveRegion(self, permissionNode); |
| 9550 | } |
| 9551 | try validateCellPayload(self, node, targetTy, permission); |
| 9552 | if let case types::PointerClass::Region(region) = class { |
| 9553 | try validateRegionStorage(self, addr.target, targetTy, region); |
| 9554 | } else if class == types::PointerClass::Owned |
| 9555 | and try containsStorageRegion(self, addr.target, targetTy) |
| 9556 | { |
| 9557 | throw emitError(self, addr.target, ErrorKind::InvalidCellPayload); |
| 9558 | } |
| 9559 | return setNodeType(self, node, Type::Cell { |
| 9560 | class, permission, payload: allocType(self, targetTy), |
| 9561 | }); |
| 9562 | } |
| 9563 | if let case Type::Array(arrayInfo) = targetTy { |
| 9564 | match addr.target.value { |
| 9565 | case ast::NodeValue::ArrayLit(_), |
| 9566 | ast::NodeValue::ArrayRepeatLit(_) => |
| 9567 | { |
| 9568 | let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.kind == ast::AddressKind::Mutable }; |
| 9569 | return setNodeType(self, node, *allocType(self, sliceTy)); |
| 9570 | } |
| 9571 | else => {} |
| 9572 | } |
| 9573 | } |
| 9574 | let pointerTy = Type::Pointer { |
| 9575 | class, target: allocType(self, targetTy), mutable: addr.kind == ast::AddressKind::Mutable, |
| 9576 | }; |
| 9577 | return setNodeType(self, node, pointerTy); |
| 9578 | } |
| 9579 | |
| 9580 | /// Analyze a dereference expression. |
| 9581 | unsafe fn resolveDeref 'arena (self: &mut Resolver 'arena, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
| 9582 | throws (ResolveError) |
| 9583 | { |
| 9584 | let operandTy = try visit(self, targetNode, hint); |
| 9585 | if let case Type::Cell { class, permission, payload } = operandTy { |
| 9586 | if class == types::PointerClass::Unsafe { |
| 9587 | try requireUnsafe(self, targetNode); |
| 9588 | } |
| 9589 | try validateCellPayload(self, node, *payload, permission); |
| 9590 | return setNodeType(self, node, *payload); |
| 9591 | } |
| 9592 | if let case Type::Pointer { class, target, .. } = operandTy { |
| 9593 | if class == types::PointerClass::Unsafe { |
| 9594 | try requireUnsafe(self, targetNode); |
| 9595 | } |
| 9596 | // Disallow dereferencing opaque pointers. |
| 9597 | if *target == Type::Opaque { |
| 9598 | throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref); |
| 9599 | } |
| 9600 | return setNodeType(self, node, *target); |
| 9601 | } |
| 9602 | // Auto-deref for single-field unlabeled records. |
| 9603 | if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy { |
| 9604 | if not recInfo.labeled and recInfo.fields.len == 1 { |
| 9605 | try requireRecordAccess(self, node, recInfo); |
| 9606 | let fieldTy = recInfo.fields[0].fieldType; |
| 9607 | setRecordFieldIndex(self, node, 0); |
| 9608 | return setNodeType(self, node, fieldTy); |
| 9609 | } |
| 9610 | } |
| 9611 | throw emitError(self, targetNode, ErrorKind::ExpectedPointer); |
| 9612 | } |
| 9613 | |
| 9614 | /// Check if a type is a pointer to opaque. |
| 9615 | fn isOpaquePointer(ty: Type) -> bool { |
| 9616 | if let case Type::Pointer { target, .. } = ty { |
| 9617 | return *target == Type::Opaque; |
| 9618 | } |
| 9619 | return false; |
| 9620 | } |
| 9621 | |
| 9622 | /// Check if a type is an opaque slice. |
| 9623 | fn isOpaqueSlice(ty: Type) -> bool { |
| 9624 | if let case Type::Slice { item, .. } = ty { |
| 9625 | return *item == Type::Opaque; |
| 9626 | } |
| 9627 | return false; |
| 9628 | } |
| 9629 | |
| 9630 | /// Check if an `as` cast between two types is valid. |
| 9631 | unsafe fn isValidCast(source: Type, target: Type) -> bool { |
| 9632 | // Allow identity casts. |
| 9633 | if source == target { |
| 9634 | return true; |
| 9635 | } |
| 9636 | // Allow numeric to numeric. |
| 9637 | if isNumericType(source) and isNumericType(target) { |
| 9638 | return true; |
| 9639 | } |
| 9640 | // Allow `void` union to numeric. |
| 9641 | // TODO: Check that variant index fits in target type. |
| 9642 | if isVoidUnion(source) and isNumericType(target) { |
| 9643 | return true; |
| 9644 | } |
| 9645 | // Allow address to numeric. |
| 9646 | if let case Type::Slice { .. } = source { |
| 9647 | // Disallow slice to numeric; slices are fat pointers. |
| 9648 | } else if isAddressType(source) and isNumericType(target) { |
| 9649 | return true; |
| 9650 | } |
| 9651 | // Allow pointer casts if one side is `*opaque` or target types are castable. |
| 9652 | if let case Type::Pointer { |
| 9653 | class: sourceClass, target: sourceTarget, mutable: sourceMutable, |
| 9654 | } = source { |
| 9655 | if let case Type::Pointer { |
| 9656 | class: targetClass, target: targetTarget, mutable: targetMutable, |
| 9657 | } = target { |
| 9658 | if sourceClass <> targetClass { |
| 9659 | return false; |
| 9660 | } |
| 9661 | if targetMutable and not sourceMutable { |
| 9662 | return false; |
| 9663 | } |
| 9664 | if isOpaquePointer(source) or isOpaquePointer(target) { |
| 9665 | return true; |
| 9666 | } |
| 9667 | return isValidCast(*sourceTarget, *targetTarget); |
| 9668 | } |
| 9669 | } |
| 9670 | // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`, |
| 9671 | // or element types are castable. |
| 9672 | if let case Type::Slice { |
| 9673 | class: sourceClass, item: sourceItem, mutable: sourceMutable, |
| 9674 | } = source { |
| 9675 | if let case Type::Slice { |
| 9676 | class: targetClass, item: targetItem, mutable: targetMutable, |
| 9677 | } = target { |
| 9678 | if sourceClass <> targetClass { |
| 9679 | return false; |
| 9680 | } |
| 9681 | if targetMutable and not sourceMutable { |
| 9682 | return false; |
| 9683 | } |
| 9684 | if isOpaqueSlice(source) or isOpaqueSlice(target) { |
| 9685 | return true; |
| 9686 | } |
| 9687 | if *targetItem == Type::U8 { |
| 9688 | return true; |
| 9689 | } |
| 9690 | return isValidCast(*sourceItem, *targetItem); |
| 9691 | } |
| 9692 | } |
| 9693 | return false; |
| 9694 | } |
| 9695 | |
| 9696 | /// Require casts into region-dependent storage to preserve its typed contents. |
| 9697 | fn regionalCastPreservesType(source: Type, target: Type) -> bool { |
| 9698 | if typesEqual(source, target) { |
| 9699 | return true; |
| 9700 | } |
| 9701 | if let case Type::Pointer { target: sourceItem, .. } = source { |
| 9702 | let case Type::Pointer { target: targetItem, .. } = target else return false; |
| 9703 | return typesEqual(*sourceItem, *targetItem); |
| 9704 | } |
| 9705 | if let case Type::Slice { item: sourceItem, .. } = source { |
| 9706 | let case Type::Slice { item: targetItem, .. } = target else return false; |
| 9707 | return typesEqual(*sourceItem, *targetItem); |
| 9708 | } |
| 9709 | return false; |
| 9710 | } |
| 9711 | |
| 9712 | /// Analyze an `as` cast expression. |
| 9713 | unsafe fn resolveAs 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: ast::As) -> Type |
| 9714 | throws (ResolveError) |
| 9715 | { |
| 9716 | let targetTy = try infer(self, expr.type); |
| 9717 | let sourceTy = try visit(self, expr.value, targetTy); |
| 9718 | if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) { |
| 9719 | try requireUnsafe(self, node); |
| 9720 | } |
| 9721 | |
| 9722 | assert sourceTy <> Type::Unknown; |
| 9723 | assert targetTy <> Type::Unknown; |
| 9724 | |
| 9725 | if let case Type::Cell { class, permission, payload } = targetTy { |
| 9726 | if let case Type::Pointer { class: sourceClass, target, mutable: true } = sourceTy; |
| 9727 | permission == nil and sourceClass == class |
| 9728 | and class <> types::PointerClass::Unsafe and typesEqual(*target, *payload) |
| 9729 | { |
| 9730 | return setNodeType(self, node, targetTy); |
| 9731 | } |
| 9732 | if typesEqual(sourceTy, targetTy) { |
| 9733 | return setNodeType(self, node, targetTy); |
| 9734 | } |
| 9735 | throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { from: sourceTy, to: targetTy })); |
| 9736 | } |
| 9737 | if containsRegion(targetTy) and not regionalCastPreservesType(sourceTy, targetTy) { |
| 9738 | throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { |
| 9739 | from: sourceTy, to: targetTy, |
| 9740 | })); |
| 9741 | } |
| 9742 | let mut valid = isValidCast(sourceTy, targetTy); |
| 9743 | if let case Type::Pointer { |
| 9744 | class: sourceClass, target: sourceTarget, mutable: sourceMutable, |
| 9745 | } = sourceTy { |
| 9746 | if let case Type::Pointer { |
| 9747 | class: targetClass, target: targetTarget, mutable: targetMutable, |
| 9748 | } = targetTy { |
| 9749 | if types::isReference(sourceClass) and |
| 9750 | targetClass == types::PointerClass::Unsafe and |
| 9751 | (not targetMutable or sourceMutable) and |
| 9752 | isValidCast(*sourceTarget, *targetTarget) |
| 9753 | { |
| 9754 | set valid = true; |
| 9755 | } |
| 9756 | } |
| 9757 | } |
| 9758 | if let case Type::Slice { |
| 9759 | class: sourceClass, item: sourceItem, mutable: sourceMutable, |
| 9760 | } = sourceTy { |
| 9761 | if let case Type::Slice { |
| 9762 | class: targetClass, item: targetItem, mutable: targetMutable, |
| 9763 | } = targetTy { |
| 9764 | if types::isReference(sourceClass) and |
| 9765 | targetClass == types::PointerClass::Unsafe and |
| 9766 | (not targetMutable or sourceMutable) and |
| 9767 | isValidCast(*sourceItem, *targetItem) |
| 9768 | { |
| 9769 | set valid = true; |
| 9770 | } |
| 9771 | } |
| 9772 | } |
| 9773 | if valid { |
| 9774 | if let case Type::Pointer { target: sourceTarget, .. } = sourceTy { |
| 9775 | if let case Type::Pointer { target: targetTarget, .. } = targetTy { |
| 9776 | if *targetTarget <> Type::Opaque and not typesEqual(*sourceTarget, *targetTarget) { |
| 9777 | try requireUnsafe(self, node); |
| 9778 | } |
| 9779 | } |
| 9780 | } |
| 9781 | if let case Type::Slice { item: sourceItem, .. } = sourceTy { |
| 9782 | if let case Type::Slice { item: targetItem, .. } = targetTy { |
| 9783 | if *targetItem <> Type::Opaque and not typesEqual(*sourceItem, *targetItem) { |
| 9784 | try requireUnsafe(self, node); |
| 9785 | } |
| 9786 | } |
| 9787 | } |
| 9788 | // Propagate the constant value after applying the cast's target-width |
| 9789 | // truncation and signed interpretation. |
| 9790 | if let value = constValueEntry(self, expr.value) { |
| 9791 | if let case ConstValue::Int(i) = value { |
| 9792 | setNodeConstValue(self, node, castConstInt(i, targetTy)); |
| 9793 | } |
| 9794 | } |
| 9795 | return setNodeType(self, node, targetTy); |
| 9796 | } |
| 9797 | throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { |
| 9798 | from: sourceTy, |
| 9799 | to: targetTy, |
| 9800 | })); |
| 9801 | } |
| 9802 | |
| 9803 | /// Analyze a range expression. |
| 9804 | unsafe fn resolveRange 'arena (self: &mut Resolver 'arena, node: *ast::Node, range: ast::Range) -> Type |
| 9805 | throws (ResolveError) |
| 9806 | { |
| 9807 | let mut start: ?*Type = nil; |
| 9808 | let mut end: ?*Type = nil; |
| 9809 | |
| 9810 | if let s = range.start { |
| 9811 | let startTy = try checkNumeric(self, s); |
| 9812 | |
| 9813 | if let e = range.end { |
| 9814 | let endTy = try checkNumeric(self, e); |
| 9815 | let mut resolvedTy = startTy; |
| 9816 | |
| 9817 | // Infer unsuffixed integer literals from the opposite bound. |
| 9818 | if startTy == Type::Int and endTy <> Type::Int { |
| 9819 | let _ = try checkAssignable(self, s, endTy); |
| 9820 | set resolvedTy = endTy; |
| 9821 | } else if endTy == Type::Int and startTy <> Type::Int { |
| 9822 | let _ = try checkAssignable(self, e, startTy); |
| 9823 | set resolvedTy = startTy; |
| 9824 | } else { |
| 9825 | let _ = try checkAssignable(self, e, startTy); |
| 9826 | } |
| 9827 | set start = allocType(self, resolvedTy); |
| 9828 | set end = allocType(self, resolvedTy); |
| 9829 | } else { |
| 9830 | set start = allocType(self, startTy); |
| 9831 | } |
| 9832 | } else if let e = range.end { |
| 9833 | set end = allocType(self, try checkNumeric(self, e)); |
| 9834 | } |
| 9835 | return setNodeType(self, node, Type::Range { start, end }); |
| 9836 | } |
| 9837 | |
| 9838 | /// Analyze a `try` expression and its handlers. |
| 9839 | /// The `expected` type is used to determine if the value is discarded (`Void`) |
| 9840 | /// or if the catch expression needs type checking. |
| 9841 | unsafe fn resolveTry 'arena (self: &mut Resolver 'arena, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type |
| 9842 | throws (ResolveError) |
| 9843 | { |
| 9844 | let call = tryExpr.expr; |
| 9845 | let case ast::NodeValue::Call(callExpr) = call.value |
| 9846 | else throw emitError(self, call, ErrorKind::TryNonThrowing); |
| 9847 | let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try, Type::Unknown); |
| 9848 | |
| 9849 | // TODO: It's annoying that we need to re-fetch the function type after |
| 9850 | // analyzing the call. |
| 9851 | let calleeTy = typeFor(self, callExpr.callee) |
| 9852 | else return setNodeType(self, node, resultTy); |
| 9853 | let case Type::Fn(calleeInfo) = calleeTy |
| 9854 | else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing); |
| 9855 | |
| 9856 | if calleeInfo.throwList.len == 0 { |
| 9857 | throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing); |
| 9858 | } |
| 9859 | // If we're not catching the error, nor panicking on error, nor returning |
| 9860 | // optional, then the current function must be able to propagate it. |
| 9861 | let mut tryResultTy = resultTy; |
| 9862 | if tryExpr.returnsOptional { |
| 9863 | // `try?` converts errors to `nil` and wraps the result in an optional. |
| 9864 | if let case Type::Optional(_) = resultTy { |
| 9865 | // Already optional, no wrapping needed. |
| 9866 | } else { |
| 9867 | set tryResultTy = Type::Optional(allocType(self, resultTy)); |
| 9868 | } |
| 9869 | } else if tryExpr.catches.len > 0 { |
| 9870 | // `try ... catch` -- one or more catch clauses. |
| 9871 | set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint); |
| 9872 | } else if not tryExpr.shouldPanic { |
| 9873 | let fnInfo = self.currentFn |
| 9874 | else throw emitError(self, node, ErrorKind::TryRequiresThrows); |
| 9875 | if fnInfo.throwList.len == 0 { |
| 9876 | throw emitError(self, node, ErrorKind::TryRequiresThrows); |
| 9877 | } |
| 9878 | // Check that *all* thrown errors of the callee can be propagated by |
| 9879 | // the caller. |
| 9880 | for throwTy in calleeInfo.throwList { |
| 9881 | let mut found = false; |
| 9882 | |
| 9883 | for callerThrowTy in fnInfo.throwList { |
| 9884 | if callerThrowTy == throwTy { |
| 9885 | set found = true; |
| 9886 | break; |
| 9887 | } |
| 9888 | } |
| 9889 | if not found { |
| 9890 | throw emitError(self, node, ErrorKind::TryIncompatibleError); |
| 9891 | } |
| 9892 | } |
| 9893 | } |
| 9894 | return setNodeType(self, node, tryResultTy); |
| 9895 | } |
| 9896 | |
| 9897 | /// Check that a `catch` body is assignable to the expected result type, but only |
| 9898 | /// in expression context (`hint` is neither `Unknown` nor `Void`). |
| 9899 | unsafe fn checkCatchBody 'arena (self: &mut Resolver 'arena, body: *ast::Node, resultTy: Type, hint: Type) |
| 9900 | throws (ResolveError) |
| 9901 | { |
| 9902 | if hint <> Type::Unknown and hint <> Type::Void { |
| 9903 | try checkAssignable(self, body, resultTy); |
| 9904 | } |
| 9905 | } |
| 9906 | |
| 9907 | /// Resolve catch clauses for a `try ... catch` expression. |
| 9908 | /// |
| 9909 | /// For a single untyped catch (with or without binding), resolves the catch |
| 9910 | /// body and returns the result type. Multi-error callees with inferred bindings |
| 9911 | /// are rejected; you must use typed catches. |
| 9912 | unsafe fn resolveTryCatches 'arena ( |
| 9913 | self: &mut Resolver 'arena, |
| 9914 | node: *ast::Node, |
| 9915 | catches: *[*ast::Node], |
| 9916 | calleeInfo: *FnType, |
| 9917 | resultTy: Type, |
| 9918 | hint: Type |
| 9919 | ) -> Type throws (ResolveError) { |
| 9920 | let firstNode = catches[0]; |
| 9921 | let case ast::NodeValue::CatchClause(first) = firstNode.value else |
| 9922 | throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode)); |
| 9923 | |
| 9924 | // Typed catches: dispatch to dedicated handler. |
| 9925 | if first.typeNode <> nil { |
| 9926 | return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint); |
| 9927 | } |
| 9928 | // Single untyped catch clause. |
| 9929 | if let binding = first.binding { |
| 9930 | if calleeInfo.throwList.len > 1 { |
| 9931 | throw emitError(self, binding, ErrorKind::TryCatchMultiError); |
| 9932 | } |
| 9933 | enterScope(self, node); |
| 9934 | |
| 9935 | let errTy = *calleeInfo.throwList[0]; |
| 9936 | try bindValueIdent(self, binding, binding, errTy, false, 0, 0); |
| 9937 | } |
| 9938 | let bodyTy = try visit(self, first.body, resultTy); |
| 9939 | |
| 9940 | if let _ = first.binding { |
| 9941 | exitScope(self); |
| 9942 | } |
| 9943 | try checkCatchBody(self, first.body, resultTy, hint); |
| 9944 | |
| 9945 | return bodyTy if resultTy == Type::Never else resultTy; |
| 9946 | } |
| 9947 | |
| 9948 | /// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`). |
| 9949 | /// |
| 9950 | /// Validates that each type annotation is in the callee's throw list, that |
| 9951 | /// there are no duplicate catch types, and that the clauses are exhaustive. |
| 9952 | unsafe fn resolveTypedCatches 'arena ( |
| 9953 | self: &mut Resolver 'arena, |
| 9954 | node: *ast::Node, |
| 9955 | catches: *[*ast::Node], |
| 9956 | calleeInfo: *FnType, |
| 9957 | resultTy: Type, |
| 9958 | hint: Type |
| 9959 | ) -> Type throws (ResolveError) { |
| 9960 | // Track which of the callee's throw types have been covered. |
| 9961 | let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS]; |
| 9962 | let mut hasCatchAll = false; |
| 9963 | let mut catchTy = Type::Never; |
| 9964 | |
| 9965 | for clauseNode in catches { |
| 9966 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value else |
| 9967 | throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode)); |
| 9968 | |
| 9969 | if let typeNode = clause.typeNode { |
| 9970 | // Typed catch clause: validate against callee's throw list. |
| 9971 | let errTy = try infer(self, typeNode); |
| 9972 | let mut foundIdx: ?u32 = nil; |
| 9973 | |
| 9974 | for throwType, j in calleeInfo.throwList { |
| 9975 | if errTy == *throwType { |
| 9976 | set foundIdx = j; |
| 9977 | break; |
| 9978 | } |
| 9979 | } |
| 9980 | let idx = foundIdx else { |
| 9981 | throw emitError(self, typeNode, ErrorKind::TryIncompatibleError); |
| 9982 | }; |
| 9983 | if covered[idx] { |
| 9984 | throw emitError(self, typeNode, ErrorKind::TryCatchDuplicateType); |
| 9985 | } |
| 9986 | set covered[idx] = true; |
| 9987 | |
| 9988 | // Bind the error variable if present. |
| 9989 | if let binding = clause.binding { |
| 9990 | enterScope(self, clauseNode); |
| 9991 | try bindValueIdent(self, binding, binding, errTy, false, 0, 0); |
| 9992 | } |
| 9993 | } else { |
| 9994 | // Catch-all clause with no type annotation or binding. |
| 9995 | set hasCatchAll = true; |
| 9996 | } |
| 9997 | // Resolve the catch body and check assignability. |
| 9998 | let bodyTy = try visit(self, clause.body, resultTy); |
| 9999 | if bodyTy <> Type::Never { set catchTy = Type::Void; } |
| 10000 | // Only typed clauses can have bindings. |
| 10001 | if let _ = clause.binding { |
| 10002 | exitScope(self); |
| 10003 | } |
| 10004 | try checkCatchBody(self, clause.body, resultTy, hint); |
| 10005 | } |
| 10006 | |
| 10007 | // Check exhaustiveness: all callee error types must be covered. |
| 10008 | if not hasCatchAll { |
| 10009 | for i in 0..calleeInfo.throwList.len { |
| 10010 | if not covered[i] { |
| 10011 | throw emitError(self, node, ErrorKind::TryCatchNonExhaustive); |
| 10012 | } |
| 10013 | } |
| 10014 | } |
| 10015 | return catchTy if resultTy == Type::Never else resultTy; |
| 10016 | } |
| 10017 | |
| 10018 | /// Analyze a `throw` statement. |
| 10019 | unsafe fn resolveThrow 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: *ast::Node) -> Type |
| 10020 | throws (ResolveError) |
| 10021 | { |
| 10022 | let fnInfo = self.currentFn |
| 10023 | else throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
| 10024 | if fnInfo.throwList.len == 0 { |
| 10025 | throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
| 10026 | } |
| 10027 | let throwTy = try infer(self, expr); |
| 10028 | for errTy in fnInfo.throwList { |
| 10029 | if let coerce = isAssignable(self, *errTy, throwTy, expr) { |
| 10030 | setNodeCoercion(self, expr, coerce); |
| 10031 | return setNodeType(self, node, Type::Never); |
| 10032 | } |
| 10033 | } |
| 10034 | throw emitError(self, expr, ErrorKind::ThrowIncompatibleError); |
| 10035 | } |
| 10036 | |
| 10037 | /// Analyze a `return` statement. |
| 10038 | unsafe fn resolveReturn 'arena (self: &mut Resolver 'arena, node: *ast::Node, retVal: ?*ast::Node) -> Type |
| 10039 | throws (ResolveError) |
| 10040 | { |
| 10041 | let f = self.currentFn |
| 10042 | else throw emitError(self, node, ErrorKind::UnexpectedReturn); |
| 10043 | let expected = *f.returnType; |
| 10044 | |
| 10045 | if let val = retVal { |
| 10046 | let actualTy = try visit(self, val, expected); |
| 10047 | if let source = referenceRegion(actualTy) { |
| 10048 | if let destination = referenceRegion(expected); |
| 10049 | not types::regionContains(source, destination) |
| 10050 | { |
| 10051 | let mut current = self.regionScope; |
| 10052 | while let scope = current { |
| 10053 | let mut ownsSource = false; |
| 10054 | for region in scope.entries { |
| 10055 | if region.id == source.id { |
| 10056 | set ownsSource = true; |
| 10057 | break; |
| 10058 | } |
| 10059 | } |
| 10060 | if ownsSource { |
| 10061 | let mut lexical: ?*unsafe mut Scope = self.scope; |
| 10062 | while let localScope = lexical { |
| 10063 | if let owner = localScope.owner { |
| 10064 | if let case ast::NodeValue::RegionBlock { bindings, .. } = |
| 10065 | owner.value |
| 10066 | { |
| 10067 | if let case NodeExtra::Regions(blockRegions) = |
| 10068 | self.nodeData.entries[owner.id].extra |
| 10069 | { |
| 10070 | if blockRegions.entries[0].id == source.id { |
| 10071 | for bindingNode in bindings { |
| 10072 | let case ast::NodeValue::RegionBinding(binding) = |
| 10073 | bindingNode.value |
| 10074 | else panic "resolveReturn: invalid region binding"; |
| 10075 | let case ast::NodeValue::AddressOf(address) = |
| 10076 | binding.value.value else continue; |
| 10077 | if let cellTy = |
| 10078 | try inferCellPayload(self, address.target) |
| 10079 | { |
| 10080 | if let case Type::Cell { |
| 10081 | permission: controlled, |
| 10082 | .. |
| 10083 | } = cellTy; controlled <> nil { |
| 10084 | throw emitError( |
| 10085 | self, |
| 10086 | val, |
| 10087 | ErrorKind::RegionEscape(source.name), |
| 10088 | ); |
| 10089 | } |
| 10090 | } |
| 10091 | } |
| 10092 | break; |
| 10093 | } |
| 10094 | } |
| 10095 | } |
| 10096 | } |
| 10097 | set lexical = localScope.parent; |
| 10098 | } |
| 10099 | break; |
| 10100 | } |
| 10101 | set current = scope.parent; |
| 10102 | } |
| 10103 | } |
| 10104 | } |
| 10105 | let _ = try expectAssignable(self, expected, actualTy, val); |
| 10106 | if isRefType(expected) and isMutablePointerLike(expected) and |
| 10107 | isMutablePointerLike(actualTy) |
| 10108 | { |
| 10109 | if not try canMutateThrough(self, val) { |
| 10110 | throw emitError(self, val, ErrorKind::ImmutableBinding); |
| 10111 | } |
| 10112 | } |
| 10113 | } else if expected <> Type::Void { |
| 10114 | throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void }); |
| 10115 | } |
| 10116 | // In throwing functions, return values are wrapped in the success variant. |
| 10117 | if f.throwList.len > 0 { |
| 10118 | setNodeCoercion(self, node, Coercion::ResultWrap); |
| 10119 | } |
| 10120 | return setNodeType(self, node, Type::Never); |
| 10121 | } |
| 10122 | |
| 10123 | /// Convert a [`ConstInt`] to its two's-complement bit pattern. |
| 10124 | fn constIntToBits(c: ConstInt) -> u64 { |
| 10125 | return (0 - c.magnitude) if c.negative else c.magnitude; |
| 10126 | } |
| 10127 | |
| 10128 | /// Convert a [`ConstInt`] to its signed two's-complement representation. |
| 10129 | fn constIntToSigned(c: ConstInt) -> i64 { |
| 10130 | return constIntToBits(c) as i64; |
| 10131 | } |
| 10132 | |
| 10133 | /// Build a [`ConstInt`] from a signed result, preserving bit width and signedness. |
| 10134 | fn constIntFromSigned(value: i64, bits: u8, signed: bool) -> ConstInt { |
| 10135 | if value < 0 { |
| 10136 | // Compute magnitude without signed overflow. |
| 10137 | let uval = value as u64; |
| 10138 | return ConstInt { |
| 10139 | magnitude: 0 - uval, |
| 10140 | bits, |
| 10141 | signed, |
| 10142 | negative: true, |
| 10143 | }; |
| 10144 | } |
| 10145 | return ConstInt { |
| 10146 | magnitude: value as u64, |
| 10147 | bits, |
| 10148 | signed, |
| 10149 | negative: false, |
| 10150 | }; |
| 10151 | } |
| 10152 | |
| 10153 | /// Build a [`ConstInt`] from a two's-complement bit pattern. |
| 10154 | fn constIntFromBits(raw: u64, bits: u8, signed: bool) -> ConstInt { |
| 10155 | let mask = parser::U64_MAX if bits == 64 else parser::U64_MAX >> (64 - bits) as u64; |
| 10156 | let truncated = raw & mask; |
| 10157 | |
| 10158 | if signed { |
| 10159 | let signBit = (mask >> 1) + 1; |
| 10160 | if (truncated & signBit) <> 0 { |
| 10161 | return ConstInt { |
| 10162 | magnitude: (0 - truncated) & mask, |
| 10163 | bits, |
| 10164 | signed, |
| 10165 | negative: true, |
| 10166 | }; |
| 10167 | } |
| 10168 | } |
| 10169 | return ConstInt { magnitude: truncated, bits, signed, negative: false }; |
| 10170 | } |
| 10171 | |
| 10172 | /// Try to fold a binary operation on two integer constants. |
| 10173 | /// Returns the resulting constant value if successful. |
| 10174 | fn foldIntBinOp(op: ast::BinaryOp, left: ConstInt, right: ConstInt) -> ?ConstValue { |
| 10175 | // Use the wider bit width and propagate signedness. |
| 10176 | let mut bits = left.bits; |
| 10177 | if right.bits > bits { |
| 10178 | set bits = right.bits; |
| 10179 | } |
| 10180 | let signed = left.signed or right.signed; |
| 10181 | let l = constIntToSigned(left); |
| 10182 | let r = constIntToSigned(right); |
| 10183 | |
| 10184 | match op { |
| 10185 | // Shift counts are masked to the left operand's width, matching |
| 10186 | // the runtime word instructions. |
| 10187 | case ast::BinaryOp::Shl => { |
| 10188 | let raw = constIntToBits(left); |
| 10189 | let shamt = constIntToBits(right) % left.bits as u64; |
| 10190 | return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed)); |
| 10191 | }, |
| 10192 | case ast::BinaryOp::Shr => { |
| 10193 | let shamt = constIntToBits(right) % left.bits as u64; |
| 10194 | if left.signed { |
| 10195 | let shifted = constIntToSigned(left) >> shamt as i64; |
| 10196 | return ConstValue::Int( |
| 10197 | constIntFromBits(shifted as u64, left.bits, true) |
| 10198 | ); |
| 10199 | } |
| 10200 | return ConstValue::Int( |
| 10201 | constIntFromBits(left.magnitude >> shamt, left.bits, false) |
| 10202 | ); |
| 10203 | }, |
| 10204 | case ast::BinaryOp::Eq => return ConstValue::Bool(l == r), |
| 10205 | case ast::BinaryOp::Ne => return ConstValue::Bool(l <> r), |
| 10206 | case ast::BinaryOp::Lt => |
| 10207 | return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude), |
| 10208 | case ast::BinaryOp::Gt => |
| 10209 | return ConstValue::Bool(l > r if signed else left.magnitude > right.magnitude), |
| 10210 | case ast::BinaryOp::Lte => |
| 10211 | return ConstValue::Bool(l <= r if signed else left.magnitude <= right.magnitude), |
| 10212 | case ast::BinaryOp::Gte => |
| 10213 | return ConstValue::Bool(l >= r if signed else left.magnitude >= right.magnitude), |
| 10214 | case ast::BinaryOp::Add => return ConstValue::Int(constIntFromSigned(l + r, bits, signed)), |
| 10215 | case ast::BinaryOp::Sub => return ConstValue::Int(constIntFromSigned(l - r, bits, signed)), |
| 10216 | case ast::BinaryOp::Mul => return ConstValue::Int(constIntFromSigned(l * r, bits, signed)), |
| 10217 | case ast::BinaryOp::Div => { |
| 10218 | if signed { |
| 10219 | if r == 0 { |
| 10220 | return nil; |
| 10221 | } |
| 10222 | return ConstValue::Int(constIntFromSigned(l / r, bits, true)); |
| 10223 | } |
| 10224 | if right.magnitude == 0 { |
| 10225 | return nil; |
| 10226 | } |
| 10227 | return constInt(left.magnitude / right.magnitude, bits, false, false); |
| 10228 | }, |
| 10229 | case ast::BinaryOp::Mod => { |
| 10230 | if signed { |
| 10231 | if r == 0 { |
| 10232 | return nil; |
| 10233 | } |
| 10234 | return ConstValue::Int(constIntFromSigned(l % r, bits, true)); |
| 10235 | } |
| 10236 | if right.magnitude == 0 { |
| 10237 | return nil; |
| 10238 | } |
| 10239 | return constInt(left.magnitude % right.magnitude, bits, false, false); |
| 10240 | }, |
| 10241 | case ast::BinaryOp::BitAnd => return ConstValue::Int(constIntFromSigned(l & r, bits, signed)), |
| 10242 | case ast::BinaryOp::BitOr => return ConstValue::Int(constIntFromSigned(l | r, bits, signed)), |
| 10243 | case ast::BinaryOp::BitXor => return ConstValue::Int(constIntFromSigned(l ^ r, bits, signed)), |
| 10244 | else => return nil, |
| 10245 | } |
| 10246 | } |
| 10247 | |
| 10248 | /// Try to constant-fold a binary operation on two resolved operands. |
| 10249 | /// Only folds when the result type is concrete. |
| 10250 | fn tryFoldBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp, resultTy: Type) { |
| 10251 | let leftVal = constValueEntry(self, binop.left) |
| 10252 | else return; |
| 10253 | let rightVal = constValueEntry(self, binop.right) |
| 10254 | else return; |
| 10255 | |
| 10256 | // Fold integer binary ops. |
| 10257 | if let case ConstValue::Int(leftInt) = leftVal { |
| 10258 | if let case ConstValue::Int(rightInt) = rightVal { |
| 10259 | if let result = foldIntBinOp(binop.op, leftInt, rightInt) { |
| 10260 | setNodeConstValue(self, node, result); |
| 10261 | } |
| 10262 | return; |
| 10263 | } |
| 10264 | } |
| 10265 | |
| 10266 | // Fold boolean binary ops. |
| 10267 | if let case ConstValue::Bool(l) = leftVal { |
| 10268 | if let case ConstValue::Bool(r) = rightVal { |
| 10269 | match binop.op { |
| 10270 | case ast::BinaryOp::And => setNodeConstValue(self, node, ConstValue::Bool(l and r)), |
| 10271 | case ast::BinaryOp::Or => setNodeConstValue(self, node, ConstValue::Bool(l or r)), |
| 10272 | case ast::BinaryOp::Eq => setNodeConstValue(self, node, ConstValue::Bool(l == r)), |
| 10273 | case ast::BinaryOp::Ne, |
| 10274 | ast::BinaryOp::Xor => setNodeConstValue(self, node, ConstValue::Bool(l <> r)), |
| 10275 | else => {} |
| 10276 | } |
| 10277 | } |
| 10278 | } |
| 10279 | } |
| 10280 | |
| 10281 | /// Analyze a binary expression. |
| 10282 | unsafe fn resolveBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp) -> Type |
| 10283 | throws (ResolveError) |
| 10284 | { |
| 10285 | let mut resultTy = Type::Unknown; |
| 10286 | |
| 10287 | match binop.op { |
| 10288 | case ast::BinaryOp::And, |
| 10289 | ast::BinaryOp::Or, |
| 10290 | ast::BinaryOp::Xor => |
| 10291 | { |
| 10292 | try checkBoolean(self, binop.left); |
| 10293 | try checkBoolean(self, binop.right); |
| 10294 | |
| 10295 | set resultTy = Type::Bool; |
| 10296 | }, |
| 10297 | case ast::BinaryOp::Eq, |
| 10298 | ast::BinaryOp::Ne => |
| 10299 | { |
| 10300 | let leftTy = try infer(self, binop.left); |
| 10301 | let rightTy = try visit(self, binop.right, leftTy); |
| 10302 | if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) { |
| 10303 | try requireUnsafe(self, node); |
| 10304 | } |
| 10305 | |
| 10306 | if not isComparable(leftTy, rightTy) { |
| 10307 | throw emitTypeMismatch(self, binop.right, TypeMismatch { |
| 10308 | expected: leftTy, |
| 10309 | actual: rightTy, |
| 10310 | }); |
| 10311 | } |
| 10312 | // When comparing `T == ?T`, record a coercion on the |
| 10313 | // non-optional side so the lowerer lifts it before comparing. |
| 10314 | // We use the already-optional type from the other side rather than |
| 10315 | // constructing a new optional, so that e.g. `?u8 == 42` coerces |
| 10316 | // `42` to `?u8` (not `?i32`). We also record OptionalLift directly |
| 10317 | // rather than using expectAssignable, because comparisons should |
| 10318 | // allow e.g. `?*mut T == *T` where mutability differs. |
| 10319 | if let case Type::Optional(_) = leftTy { |
| 10320 | if not isOptionalType(rightTy) { |
| 10321 | setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy)); |
| 10322 | } |
| 10323 | } else if let case Type::Optional(_) = rightTy { |
| 10324 | setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy)); |
| 10325 | } |
| 10326 | set resultTy = Type::Bool; |
| 10327 | }, |
| 10328 | else => { |
| 10329 | // Check for pointer arithmetic before numeric check. |
| 10330 | if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub { |
| 10331 | let leftTy = try infer(self, binop.left); |
| 10332 | let rightTy = try visit(self, binop.right, leftTy); |
| 10333 | |
| 10334 | // Allow arithmetic on owning pointers and unsafe pointers, but |
| 10335 | // never on references. |
| 10336 | if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy { |
| 10337 | if *leftTarget == Type::Opaque { |
| 10338 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
| 10339 | } |
| 10340 | if not types::isReference(leftClass) |
| 10341 | and isNumericType(rightTy) |
| 10342 | { |
| 10343 | try requireUnsafe(self, node); |
| 10344 | return setNodeType(self, node, leftTy); |
| 10345 | } |
| 10346 | } |
| 10347 | if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy { |
| 10348 | if *rightTarget == Type::Opaque { |
| 10349 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
| 10350 | } |
| 10351 | if binop.op == ast::BinaryOp::Add |
| 10352 | and not types::isReference(rightClass) |
| 10353 | and isNumericType(leftTy) |
| 10354 | { |
| 10355 | try requireUnsafe(self, node); |
| 10356 | return setNodeType(self, node, rightTy); |
| 10357 | } |
| 10358 | } |
| 10359 | } |
| 10360 | let leftTy = try checkNumeric(self, binop.left); |
| 10361 | let rightTy = try checkNumeric(self, binop.right); |
| 10362 | |
| 10363 | let mut operandTy = leftTy; |
| 10364 | if leftTy <> rightTy { |
| 10365 | if leftTy == Type::Int { |
| 10366 | set operandTy = rightTy; |
| 10367 | } else if rightTy <> Type::Int { |
| 10368 | throw emitTypeMismatch(self, binop.right, TypeMismatch { |
| 10369 | expected: leftTy, |
| 10370 | actual: rightTy, |
| 10371 | }); |
| 10372 | } |
| 10373 | } |
| 10374 | |
| 10375 | // Ordering comparisons return `bool`, not the operand type. |
| 10376 | match binop.op { |
| 10377 | case ast::BinaryOp::Lt, ast::BinaryOp::Gt, |
| 10378 | ast::BinaryOp::Lte, ast::BinaryOp::Gte => |
| 10379 | set resultTy = Type::Bool, |
| 10380 | else => |
| 10381 | set resultTy = operandTy, |
| 10382 | } |
| 10383 | |
| 10384 | } |
| 10385 | }; |
| 10386 | // Try constant folding after both operands are resolved. |
| 10387 | tryFoldBinOp(self, node, binop, resultTy); |
| 10388 | |
| 10389 | return setNodeType(self, node, resultTy); |
| 10390 | } |
| 10391 | |
| 10392 | /// Analyze a unary expression. |
| 10393 | unsafe fn resolveUnOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, unop: ast::UnOp) -> Type |
| 10394 | throws (ResolveError) |
| 10395 | { |
| 10396 | let mut resultTy = Type::Unknown; |
| 10397 | |
| 10398 | match unop.op { |
| 10399 | case ast::UnaryOp::Not => { |
| 10400 | set resultTy = try checkBoolean(self, unop.value); |
| 10401 | if let value = constValueEntry(self, unop.value) { |
| 10402 | if let case ConstValue::Bool(val) = value { |
| 10403 | setNodeConstValue(self, node, ConstValue::Bool(not val)); |
| 10404 | } |
| 10405 | } |
| 10406 | }, |
| 10407 | case ast::UnaryOp::Neg => { |
| 10408 | // TODO: Check that we're allowed to use `-` here? Should negation |
| 10409 | // only be valid for signed integers? |
| 10410 | set resultTy = try checkNumeric(self, unop.value); |
| 10411 | if let value = constValueEntry(self, unop.value) { |
| 10412 | // Get the constant expression for the value, flip the sign, |
| 10413 | // and store that new expression on the unary op node. |
| 10414 | if let case ConstValue::Int(intVal) = value { |
| 10415 | setNodeConstValue( |
| 10416 | self, |
| 10417 | node, |
| 10418 | constInt(intVal.magnitude, intVal.bits, true, not intVal.negative) |
| 10419 | ); |
| 10420 | } |
| 10421 | } |
| 10422 | }, |
| 10423 | case ast::UnaryOp::BitNot => { |
| 10424 | set resultTy = try checkNumeric(self, unop.value); |
| 10425 | if let value = constValueEntry(self, unop.value) { |
| 10426 | if let case ConstValue::Int(intVal) = value { |
| 10427 | let signed = constIntToSigned(intVal); |
| 10428 | let inverted = constIntFromSigned(-(signed + 1), intVal.bits, intVal.signed); |
| 10429 | setNodeConstValue(self, node, ConstValue::Int(inverted)); |
| 10430 | } |
| 10431 | } |
| 10432 | }, |
| 10433 | }; |
| 10434 | return setNodeType(self, node, resultTy); |
| 10435 | } |
| 10436 | |
| 10437 | /// Resolve a type signature node and set its type. |
| 10438 | unsafe fn inferTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type |
| 10439 | throws (ResolveError) |
| 10440 | { |
| 10441 | let resolved = try resolveTypeSig(self, node, sig); |
| 10442 | |
| 10443 | return setNodeType(self, node, resolved); |
| 10444 | } |
| 10445 | |
| 10446 | /// Convert a parsed pointer qualifier to its semantic class. |
| 10447 | fn resolvePointerClass(class: ast::PointerClass) -> types::PointerClass { |
| 10448 | match class { |
| 10449 | case ast::PointerClass::Owned => return types::PointerClass::Owned, |
| 10450 | case ast::PointerClass::Ref => return types::PointerClass::Ref, |
| 10451 | case ast::PointerClass::Unsafe => return types::PointerClass::Unsafe, |
| 10452 | } |
| 10453 | } |
| 10454 | |
| 10455 | /// Convert a type signature node into a type value. |
| 10456 | unsafe fn resolveTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type |
| 10457 | throws (ResolveError) |
| 10458 | { |
| 10459 | match sig { |
| 10460 | case ast::TypeSig::Cell { class, permission, payload } => { |
| 10461 | let inner = try infer(self, payload); |
| 10462 | try ensureStorableType(self, node, inner); |
| 10463 | let mut permissionRegion: ?*unsafe types::Region = nil; |
| 10464 | if let permissionNode = permission { |
| 10465 | set permissionRegion = try resolveRegion(self, permissionNode); |
| 10466 | } |
| 10467 | let allocator = alloc::arenaAllocator(self.arena); |
| 10468 | self.cellChecks.append(CellCheck { |
| 10469 | node, |
| 10470 | payload: inner, |
| 10471 | permission: permissionRegion, |
| 10472 | regions: self.regionScope, |
| 10473 | moduleId: self.currentMod, |
| 10474 | }, allocator); |
| 10475 | return Type::Cell { |
| 10476 | class: resolvePointerClass(class), |
| 10477 | permission: permissionRegion, |
| 10478 | payload: allocType(self, inner), |
| 10479 | }; |
| 10480 | } |
| 10481 | case ast::TypeSig::RegionRef { region, type } => { |
| 10482 | let identity = try resolveRegion(self, region); |
| 10483 | let base = try infer(self, type); |
| 10484 | match base { |
| 10485 | case Type::Cell { permission, payload, .. } => |
| 10486 | return Type::Cell { |
| 10487 | class: types::PointerClass::Region(identity), permission, payload, |
| 10488 | }, |
| 10489 | case Type::Pointer { target, mutable, .. } => |
| 10490 | return Type::Pointer { class: types::PointerClass::Region(identity), target, mutable }, |
| 10491 | case Type::Slice { item, mutable, .. } => |
| 10492 | return Type::Slice { class: types::PointerClass::Region(identity), item, mutable }, |
| 10493 | case Type::TraitObject { traitInfo, mutable, .. } => |
| 10494 | return Type::TraitObject { class: types::PointerClass::Region(identity), traitInfo, mutable }, |
| 10495 | else => throw emitError(self, node, ErrorKind::InvalidRefPosition), |
| 10496 | } |
| 10497 | } |
| 10498 | case ast::TypeSig::Applied { name, regions } => { |
| 10499 | if let case ast::NodeValue::Ident(spelling) = name.value; |
| 10500 | mem::eq(spelling, "Session") and findTypeSymbol(self.scope, spelling) == nil |
| 10501 | { |
| 10502 | if regions.len <> 1 { |
| 10503 | throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { |
| 10504 | expected: 1, actual: regions.len, |
| 10505 | })); |
| 10506 | } |
| 10507 | return Type::Session(try resolveRegion(self, regions[0])); |
| 10508 | } |
| 10509 | let base = try resolveTypeName(self, name); |
| 10510 | return Type::Nominal(try applyNominalRegions(self, base, regions, node)); |
| 10511 | } |
| 10512 | case ast::TypeSig::Void => { |
| 10513 | return Type::Void; |
| 10514 | } |
| 10515 | case ast::TypeSig::Never => { |
| 10516 | return Type::Never; |
| 10517 | } |
| 10518 | case ast::TypeSig::Opaque => { |
| 10519 | return Type::Opaque; |
| 10520 | } |
| 10521 | case ast::TypeSig::Bool => { |
| 10522 | return Type::Bool; |
| 10523 | } |
| 10524 | case ast::TypeSig::Integer { width, sign } => { |
| 10525 | let u = sign == ast::Signedness::Unsigned; |
| 10526 | match width { |
| 10527 | case 1 => return Type::U8 if u else Type::I8, |
| 10528 | case 2 => return Type::U16 if u else Type::I16, |
| 10529 | case 4 => return Type::U32 if u else Type::I32, |
| 10530 | case 8 => return Type::U64 if u else Type::I64, |
| 10531 | else => { |
| 10532 | panic "resolveTypeSig: invalid integer width"; |
| 10533 | } |
| 10534 | } |
| 10535 | } |
| 10536 | case ast::TypeSig::Array { itemType, length } => { |
| 10537 | let item = try infer(self, itemType); |
| 10538 | let length = try checkSizeInt(self, length); |
| 10539 | |
| 10540 | return Type::Array(ArrayType { item: allocType(self, item), length }); |
| 10541 | } |
| 10542 | case ast::TypeSig::Slice { class, itemType, mutable } => { |
| 10543 | let item = try infer(self, itemType); |
| 10544 | return Type::Slice { |
| 10545 | class: resolvePointerClass(class), |
| 10546 | item: allocType(self, item), |
| 10547 | mutable, |
| 10548 | }; |
| 10549 | } |
| 10550 | case ast::TypeSig::Pointer { class, valueType, mutable } => { |
| 10551 | let target = try infer(self, valueType); |
| 10552 | return Type::Pointer { |
| 10553 | class: resolvePointerClass(class), |
| 10554 | target: allocType(self, target), |
| 10555 | mutable, |
| 10556 | }; |
| 10557 | } |
| 10558 | case ast::TypeSig::Optional { valueType } => { |
| 10559 | let payload = try infer(self, valueType); |
| 10560 | return Type::Optional(allocType(self, payload)); |
| 10561 | } |
| 10562 | case ast::TypeSig::Nominal(name) => { |
| 10563 | let ty = try resolveTypeName(self, name); |
| 10564 | try requireNominalArguments(self, ty, node); |
| 10565 | return Type::Nominal(ty); |
| 10566 | } |
| 10567 | case ast::TypeSig::Record { fields, labeled } => { |
| 10568 | let mut recordType = try resolveRecordFields(self, node, fields, labeled); |
| 10569 | set recordType.declaredCopy = true; |
| 10570 | for field in recordType.fields { |
| 10571 | if not isCopy(field.fieldType) { |
| 10572 | set recordType.declaredCopy = false; |
| 10573 | } |
| 10574 | } |
| 10575 | set recordType.regions = self.regionScope; |
| 10576 | let nominalTy = allocNominalType(self, NominalType::Record(recordType)); |
| 10577 | if let scope = self.regionScope { |
| 10578 | let map = regionSubstitution(self, scope); |
| 10579 | for parameter, i in scope.entries { |
| 10580 | set map.arguments[i] = parameter; |
| 10581 | } |
| 10582 | return Type::Nominal(internNominalApplication(self, nominalTy, &map)); |
| 10583 | } |
| 10584 | return Type::Nominal(nominalTy); |
| 10585 | } |
| 10586 | case ast::TypeSig::Fn { sig: t, isUnsafe } => { |
| 10587 | let a = alloc::arenaAllocator(self.arena); |
| 10588 | let mut paramTypes: *mut [*Type] = &mut []; |
| 10589 | let mut throwList: *mut [*Type] = &mut []; |
| 10590 | |
| 10591 | if t.params.len > MAX_FN_PARAMS { |
| 10592 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
| 10593 | expected: MAX_FN_PARAMS, |
| 10594 | actual: t.params.len, |
| 10595 | })); |
| 10596 | } |
| 10597 | if t.throwList.len > MAX_FN_THROWS { |
| 10598 | throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch { |
| 10599 | expected: MAX_FN_THROWS, |
| 10600 | actual: t.throwList.len, |
| 10601 | })); |
| 10602 | } |
| 10603 | |
| 10604 | for paramNode in t.params { |
| 10605 | let paramTy = try resolveValueType(self, paramNode); |
| 10606 | paramTypes.append(allocType(self, paramTy), a); |
| 10607 | } |
| 10608 | for tyNode in t.throwList { |
| 10609 | let throwTy = try resolveValueType(self, tyNode); |
| 10610 | try ensureStorableType(self, tyNode, throwTy); |
| 10611 | try validateErrorTag(self, tyNode, throwTy, &throwList[..]); |
| 10612 | throwList.append(allocType(self, throwTy), a); |
| 10613 | } |
| 10614 | let mut retType = allocType(self, Type::Void); |
| 10615 | if let ret = t.returnType { |
| 10616 | let resolvedRet = try resolveValueType(self, ret); |
| 10617 | try ensureStorableType(self, ret, resolvedRet); |
| 10618 | set retType = allocType(self, resolvedRet); |
| 10619 | } |
| 10620 | let fnType = FnType { |
| 10621 | regions: nil, |
| 10622 | paramTypes: ¶mTypes[..], |
| 10623 | returnType: retType, |
| 10624 | throwList: &throwList[..], |
| 10625 | isUnsafe, |
| 10626 | }; |
| 10627 | return Type::Fn(allocFnType(self, fnType)); |
| 10628 | } |
| 10629 | // Resolve an opaque trait object signature. |
| 10630 | case ast::TypeSig::TraitObject { class, traitName, mutable } => { |
| 10631 | let sym = try resolveNamePath(self, traitName); |
| 10632 | let case SymbolData::Trait(traitInfo) = sym.data |
| 10633 | else throw emitError(self, traitName, ErrorKind::Internal); |
| 10634 | setNodeSymbol(self, traitName, sym); |
| 10635 | |
| 10636 | return Type::TraitObject { class: resolvePointerClass(class), traitInfo, mutable }; |
| 10637 | } |
| 10638 | } |
| 10639 | } |
| 10640 | |
| 10641 | /// Check if a type can be used for inferrence. |
| 10642 | fn isTypeInferrable(type: Type) -> bool { |
| 10643 | if let case Type::Pointer { target, .. } = type { |
| 10644 | return isTypeInferrable(*target); |
| 10645 | } |
| 10646 | match type { |
| 10647 | case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false, |
| 10648 | case Type::Array(ary) => return isTypeInferrable(*ary.item), |
| 10649 | case Type::Optional(opt) => return isTypeInferrable(*opt), |
| 10650 | else => return true, |
| 10651 | } |
| 10652 | } |
| 10653 | |
| 10654 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
| 10655 | export unsafe fn resolveExpr 'arena ( |
| 10656 | self: &mut Resolver 'arena, expr: *ast::Node, arena: &mut ast::NodeArena |
| 10657 | ) -> Diagnostics throws (ResolveError) { |
| 10658 | let a = alloc::arenaAllocator(&mut arena.arena); |
| 10659 | let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr)); |
| 10660 | let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a); |
| 10661 | let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts); |
| 10662 | |
| 10663 | let case ast::NodeValue::Block(block) = module.modBody.value |
| 10664 | else panic "resolveExpr: expected block for module body"; |
| 10665 | enterScope(self, module.modBody); |
| 10666 | try resolveModuleDecls(self, &block) catch { |
| 10667 | return diagnostics(self); |
| 10668 | }; |
| 10669 | try resolveModuleDefs(self, &block) catch { |
| 10670 | return diagnostics(self); |
| 10671 | }; |
| 10672 | exitScope(self); |
| 10673 | |
| 10674 | return diagnostics(self); |
| 10675 | } |
| 10676 | |
| 10677 | /// Analyze a parsed module root, ie. a block of top-level statements. |
| 10678 | export unsafe fn resolveModuleRoot 'arena (self: &mut Resolver 'arena, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
| 10679 | let case ast::NodeValue::Block(block) = root.value |
| 10680 | else panic "resolveModuleRoot: expected block for module root"; |
| 10681 | |
| 10682 | enterScope(self, root); |
| 10683 | try resolveModuleDecls(self, &block) catch { |
| 10684 | return diagnostics(self); |
| 10685 | }; |
| 10686 | try resolveModuleDefs(self, &block) catch { |
| 10687 | return diagnostics(self); |
| 10688 | }; |
| 10689 | exitScope(self); |
| 10690 | setNodeType(self, root, Type::Void); |
| 10691 | |
| 10692 | return diagnostics(self); |
| 10693 | } |
| 10694 | |
| 10695 | /// Analyze the module graph. This pass processes `mod` statements, creating symbols |
| 10696 | /// and scopes for them, and also binds type names in each module so that cross-module |
| 10697 | /// type references work regardless of declaration order. |
| 10698 | unsafe fn resolveModuleGraph 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
| 10699 | try bindTypeNames(self, block); |
| 10700 | |
| 10701 | for node in block.statements { |
| 10702 | if let case ast::NodeValue::Mod(decl) = node.value { |
| 10703 | try resolveModGraph(self, node, decl); |
| 10704 | } |
| 10705 | } |
| 10706 | } |
| 10707 | |
| 10708 | /// Bind all type names in a module. |
| 10709 | /// Skips declarations that have already been bound. |
| 10710 | unsafe fn bindTypeNames 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
| 10711 | for node in block.statements { |
| 10712 | match node.value { |
| 10713 | case ast::NodeValue::RecordDecl(decl) => { |
| 10714 | if symbolFor(self, node) == nil { |
| 10715 | try bindTypeName(self, node, decl.name, decl.attrs) catch {}; |
| 10716 | } |
| 10717 | } |
| 10718 | case ast::NodeValue::UnionDecl(decl) => { |
| 10719 | if symbolFor(self, node) == nil { |
| 10720 | try bindTypeName(self, node, decl.name, decl.attrs) catch {}; |
| 10721 | } |
| 10722 | } |
| 10723 | case ast::NodeValue::TraitDecl { name, attrs, .. } => { |
| 10724 | if symbolFor(self, node) == nil { |
| 10725 | try bindTraitName(self, node, name, attrs) catch {}; |
| 10726 | } |
| 10727 | } |
| 10728 | else => {} |
| 10729 | } |
| 10730 | } |
| 10731 | } |
| 10732 | |
| 10733 | /// Resolve all type bodies in a module. |
| 10734 | unsafe fn resolveTypeBodies 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
| 10735 | for node in block.statements { |
| 10736 | match node.value { |
| 10737 | case ast::NodeValue::RecordDecl(decl) => { |
| 10738 | try resolveRecordBody(self, node, decl) catch { |
| 10739 | // Continue resolving other types even if one fails. |
| 10740 | }; |
| 10741 | } |
| 10742 | case ast::NodeValue::UnionDecl(decl) => { |
| 10743 | try resolveUnionBody(self, node, decl) catch { |
| 10744 | // Continue resolving other types even if one fails. |
| 10745 | }; |
| 10746 | } |
| 10747 | case ast::NodeValue::TraitDecl { supertraits, methods, .. } => { |
| 10748 | try resolveTraitBody(self, node, supertraits, methods) catch { |
| 10749 | // Continue resolving other types even if one fails. |
| 10750 | }; |
| 10751 | } |
| 10752 | else => { |
| 10753 | // Ignore other declarations. |
| 10754 | } |
| 10755 | } |
| 10756 | } |
| 10757 | } |
| 10758 | |
| 10759 | /// Analyze module declarations. This pass processes all top-level statements. When it hits |
| 10760 | /// a `mod` statement, it recurses inside the module, analyzing its statements. Module import |
| 10761 | /// statements (`use`) are processed here, and make use of the module graph established in the |
| 10762 | /// previous pass. |
| 10763 | /// |
| 10764 | /// This function uses a two-phase approach: |
| 10765 | /// Phase 1: Bind all type names to allow forward references and mutual recursion. |
| 10766 | /// Phase 2: Resolve type bodies, ie. field types, variant types, etc. |
| 10767 | unsafe fn resolveModuleDecls 'arena (res: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
| 10768 | // Phase 1: Bind all type names as placeholders. |
| 10769 | try bindTypeNames(res, block); |
| 10770 | // Phase 2: Process imports so names available from the module graph can |
| 10771 | // be used in function signatures. |
| 10772 | for node in block.statements { |
| 10773 | if let case ast::NodeValue::Use(decl) = node.value { |
| 10774 | try resolveUse(res, node, decl); |
| 10775 | } |
| 10776 | } |
| 10777 | // Phase 3: Bind function signatures so that function references are |
| 10778 | // available in constant and static initializers. |
| 10779 | for node in block.statements { |
| 10780 | if let case ast::NodeValue::FnDecl(decl) = node.value { |
| 10781 | try resolveFnDecl(res, node, decl); |
| 10782 | } |
| 10783 | } |
| 10784 | // Phase 4: Process constants before submodules, so that child modules |
| 10785 | // can reference parent constants via `super::`. |
| 10786 | for node in block.statements { |
| 10787 | if let case ast::NodeValue::ConstDecl(_) = node.value { |
| 10788 | try infer(res, node); |
| 10789 | } |
| 10790 | } |
| 10791 | // Phase 5: Process submodule declarations -- recurses into child modules. |
| 10792 | // Child modules may trigger on-demand type resolution via |
| 10793 | // [`ensureNominalResolved`] which switches to the declaring module's |
| 10794 | // scope. |
| 10795 | for node in block.statements { |
| 10796 | if let case ast::NodeValue::Mod(decl) = node.value { |
| 10797 | try resolveModDecl(res, node, decl); |
| 10798 | } |
| 10799 | } |
| 10800 | // Phase 5b: Process wildcard imports after submodules are resolved, |
| 10801 | // so that transitive re-exports (export use foo::*) are visible. |
| 10802 | for node in block.statements { |
| 10803 | if let case ast::NodeValue::Use(decl) = node.value { |
| 10804 | if decl.wildcard { |
| 10805 | try resolveUse(res, node, decl); |
| 10806 | } |
| 10807 | } |
| 10808 | } |
| 10809 | // Phase 6: Resolve type bodies (record fields, union variants). |
| 10810 | try resolveTypeBodies(res, block); |
| 10811 | // Phase 7: Process all other declarations (statics, etc.). |
| 10812 | for stmt in block.statements { |
| 10813 | try visitDecl(res, stmt); |
| 10814 | } |
| 10815 | } |
| 10816 | |
| 10817 | /// Create a place with no storage root or field projections. |
| 10818 | fn emptyBorrowPlace() -> BorrowPlace { |
| 10819 | return BorrowPlace { root: nil, fields: [0; MAX_BORROW_FIELDS], len: 0, precise: true }; |
| 10820 | } |
| 10821 | |
| 10822 | /// Create initialized loan and loop scratch state for one function. |
| 10823 | fn linearChecker 'arena 'checking ( |
| 10824 | resolver: &'checking mut Resolver 'arena, regions: ?*RegionScope |
| 10825 | ) -> LinearChecker 'arena 'checking where 'arena: 'checking { |
| 10826 | let place = emptyBorrowPlace(); |
| 10827 | return LinearChecker 'arena 'checking { |
| 10828 | resolver, |
| 10829 | regional: [nil; MAX_REGIONAL_LOANS], |
| 10830 | regionalLen: 0, |
| 10831 | regions, |
| 10832 | loopBackLoans: [0; MAX_LINEAR_LOOP_DEPTH], |
| 10833 | loopExitLoans: [0; MAX_LINEAR_LOOP_DEPTH], |
| 10834 | loopRegions: [nil; MAX_LINEAR_LOOP_DEPTH], |
| 10835 | loans: [place; MAX_LINEAR_BINDINGS], |
| 10836 | loanLen: 0, |
| 10837 | locals: [LocalLoan { |
| 10838 | binding: nil, place, exclusive: false, permission: nil, storage: nil, |
| 10839 | }; MAX_LINEAR_BINDINGS], |
| 10840 | localLen: 0, |
| 10841 | authorityPermissions: [nil; MAX_LINEAR_BINDINGS], |
| 10842 | authorityBindings: [nil; MAX_LINEAR_BINDINGS], |
| 10843 | authorityExclusive: [false; MAX_LINEAR_BINDINGS], |
| 10844 | authorityLen: 0, |
| 10845 | payloadAddress: nil, |
| 10846 | witnessPermission: nil, |
| 10847 | loopMarks: [0; MAX_LINEAR_LOOP_DEPTH], |
| 10848 | loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH], |
| 10849 | loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH], |
| 10850 | loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH], |
| 10851 | loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH], |
| 10852 | loopDepth: 0, |
| 10853 | }; |
| 10854 | } |
| 10855 | |
| 10856 | /// Create an empty ownership environment with initialized binding slots. |
| 10857 | fn linearEnv() -> LinearEnv { |
| 10858 | return LinearEnv { |
| 10859 | regionalLoans: 0, |
| 10860 | symbols: [nil; MAX_LINEAR_BINDINGS], |
| 10861 | available: 0, |
| 10862 | len: 0, |
| 10863 | terminated: false, |
| 10864 | }; |
| 10865 | } |
| 10866 | |
| 10867 | /// Read initialized binding metadata from the active prefix. |
| 10868 | fn linearSymbol(env: &LinearEnv, index: u32) -> TrackedSymbol { |
| 10869 | assert index < env.len, "linearSymbol: binding index outside active prefix"; |
| 10870 | let symbol = env.symbols[index] else panic "linearSymbol: missing active binding"; |
| 10871 | return symbol; |
| 10872 | } |
| 10873 | |
| 10874 | /// Find a tracked binding by symbol identity. |
| 10875 | fn findLinearBinding(env: &LinearEnv, symbolId: u32) -> ?u32 { |
| 10876 | for i in 0..env.len { |
| 10877 | if linearSymbol(env, i).id == symbolId { |
| 10878 | return i; |
| 10879 | } |
| 10880 | } |
| 10881 | return nil; |
| 10882 | } |
| 10883 | |
| 10884 | /// Return whether a tracked binding is still available. |
| 10885 | fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool { |
| 10886 | return (env.available & ((1 as u64) << (index as u64))) <> 0; |
| 10887 | } |
| 10888 | |
| 10889 | /// Find the newest live authority for a cell permission. |
| 10890 | unsafe fn findCellAuthority 'arena 'checking ( |
| 10891 | checker: &LinearChecker 'arena 'checking, |
| 10892 | env: &LinearEnv, |
| 10893 | permission: *unsafe types::Region, |
| 10894 | ) -> ?u32 where 'arena: 'checking { |
| 10895 | let mut i = checker.authorityLen; |
| 10896 | while i > 0 { |
| 10897 | set i -= 1; |
| 10898 | let candidate = checker.authorityPermissions[i] else continue; |
| 10899 | if candidate.id <> permission.id { |
| 10900 | continue; |
| 10901 | } |
| 10902 | let binding = checker.authorityBindings[i] |
| 10903 | else panic "findCellAuthority: missing authority binding"; |
| 10904 | if let bindingIndex = findLinearBinding(env, binding.id); |
| 10905 | not linearBindingAvailable(env, bindingIndex) |
| 10906 | { |
| 10907 | continue; |
| 10908 | } |
| 10909 | return i; |
| 10910 | } |
| 10911 | return nil; |
| 10912 | } |
| 10913 | |
| 10914 | /// Add a local binding when its resolved type moves by value. |
| 10915 | unsafe fn addLinearBinding 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
| 10916 | throws (ResolveError) where 'arena: 'checking |
| 10917 | { |
| 10918 | let sym = symbolFor(checker.resolver, node) else return; |
| 10919 | let case SymbolData::Value { type: ty, .. } = sym.data else return; |
| 10920 | if not isMoveOnly(ty) { |
| 10921 | return; |
| 10922 | } |
| 10923 | if env.len >= MAX_LINEAR_BINDINGS { |
| 10924 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
| 10925 | } |
| 10926 | let usage = BindingUse::Linear if isLinear(ty) else BindingUse::Affine; |
| 10927 | set env.symbols[env.len] = TrackedSymbol { id: sym.id, name: sym.name, node: sym.node, usage }; |
| 10928 | set env.available |= (1 as u64) << (env.len as u64); |
| 10929 | set env.len += 1; |
| 10930 | } |
| 10931 | |
| 10932 | /// Mark a tracked binding as uninitialized. |
| 10933 | fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) { |
| 10934 | let binding = self.nodeData.entries[node.id].binding else return; |
| 10935 | let index = findLinearBinding(env, binding.id) else return; |
| 10936 | set env.available &= ~((1 as u64) << (index as u64)); |
| 10937 | } |
| 10938 | |
| 10939 | /// Require exact-use bindings introduced after `start` to be consumed. |
| 10940 | fn finishLinearScope 'arena 'checking ( |
| 10941 | checker: &mut LinearChecker 'arena 'checking, |
| 10942 | env: &mut LinearEnv, |
| 10943 | start: u32, |
| 10944 | ) throws (ResolveError) where 'arena: 'checking { |
| 10945 | if not env.terminated { |
| 10946 | for i in start..env.len { |
| 10947 | if linearBindingAvailable(env, i) { |
| 10948 | let sym = linearSymbol(env, i); |
| 10949 | if sym.usage == BindingUse::Linear { |
| 10950 | throw emitError( |
| 10951 | checker.resolver, |
| 10952 | sym.node, |
| 10953 | ErrorKind::LinearNotConsumed(sym.name), |
| 10954 | ); |
| 10955 | } |
| 10956 | } |
| 10957 | } |
| 10958 | } |
| 10959 | set env.len = start; |
| 10960 | } |
| 10961 | |
| 10962 | /// Require a tracked identifier to remain available for any access. |
| 10963 | fn checkLinearIdent 'arena 'checking ( |
| 10964 | checker: &mut LinearChecker 'arena 'checking, |
| 10965 | env: &mut LinearEnv, |
| 10966 | node: *ast::Node, |
| 10967 | ) throws (ResolveError) where 'arena: 'checking { |
| 10968 | let binding = checker.resolver.nodeData.entries[node.id].binding else return; |
| 10969 | let index = findLinearBinding(env, binding.id) else return; |
| 10970 | if not linearBindingAvailable(env, index) { |
| 10971 | let sym = linearSymbol(env, index); |
| 10972 | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if sym.usage == BindingUse::Linear |
| 10973 | else ErrorKind::AffineUseAfterMove(sym.name); |
| 10974 | throw emitError(checker.resolver, node, kind); |
| 10975 | } |
| 10976 | } |
| 10977 | |
| 10978 | /// Move or consume a tracked identifier once. |
| 10979 | fn consumeLinearIdent 'arena 'checking ( |
| 10980 | checker: &mut LinearChecker 'arena 'checking, |
| 10981 | env: &mut LinearEnv, |
| 10982 | node: *ast::Node, |
| 10983 | ) throws (ResolveError) where 'arena: 'checking { |
| 10984 | try checkLinearIdent(checker, env, node); |
| 10985 | let binding = checker.resolver.nodeData.entries[node.id].binding else return; |
| 10986 | let index = findLinearBinding(env, binding.id) else return; |
| 10987 | set env.available &= ~((1 as u64) << (index as u64)); |
| 10988 | } |
| 10989 | |
| 10990 | /// Merge ownership availability across two live branches. |
| 10991 | /// Validate both inputs before writing to an output that can alias either input. |
| 10992 | fn joinLinearBranches 'arena 'checking ( |
| 10993 | checker: &mut LinearChecker 'arena 'checking, |
| 10994 | env: &mut LinearEnv, |
| 10995 | left: &LinearEnv, |
| 10996 | right: &LinearEnv, |
| 10997 | node: *ast::Node, |
| 10998 | ) throws (ResolveError) where 'arena: 'checking { |
| 10999 | if left.terminated and right.terminated { |
| 11000 | set *env = *left; |
| 11001 | set env.terminated = true; |
| 11002 | return; |
| 11003 | } |
| 11004 | if left.terminated { |
| 11005 | set *env = *right; |
| 11006 | return; |
| 11007 | } |
| 11008 | if right.terminated { |
| 11009 | set *env = *left; |
| 11010 | return; |
| 11011 | } |
| 11012 | assert left.len == right.len, "joinLinearBranches: scope mismatch"; |
| 11013 | let mut available = left.available; |
| 11014 | for i in 0..left.len { |
| 11015 | if linearBindingAvailable(left, i) <> linearBindingAvailable(right, i) { |
| 11016 | let sym = linearSymbol(left, i); |
| 11017 | if sym.usage == BindingUse::Linear { |
| 11018 | throw emitError( |
| 11019 | checker.resolver, |
| 11020 | node, |
| 11021 | ErrorKind::LinearBranchMismatch(sym.name), |
| 11022 | ); |
| 11023 | } |
| 11024 | set available &= ~((1 as u64) << (i as u64)); |
| 11025 | } |
| 11026 | } |
| 11027 | let regionalLoans = left.regionalLoans | right.regionalLoans; |
| 11028 | set *env = *left; |
| 11029 | set env.available = available; |
| 11030 | set env.regionalLoans = regionalLoans; |
| 11031 | } |
| 11032 | |
| 11033 | /// Require all available exact-use bindings to be consumed at a function exit. |
| 11034 | fn finishLinearExit 'arena 'checking ( |
| 11035 | checker: &mut LinearChecker 'arena 'checking, |
| 11036 | env: &mut LinearEnv, |
| 11037 | ) throws (ResolveError) where 'arena: 'checking { |
| 11038 | if env.terminated { |
| 11039 | return; |
| 11040 | } |
| 11041 | for i in 0..env.len { |
| 11042 | if linearBindingAvailable(env, i) { |
| 11043 | let sym = linearSymbol(env, i); |
| 11044 | if sym.usage == BindingUse::Linear { |
| 11045 | throw emitError( |
| 11046 | checker.resolver, |
| 11047 | sym.node, |
| 11048 | ErrorKind::LinearNotConsumed(sym.name), |
| 11049 | ); |
| 11050 | } |
| 11051 | } |
| 11052 | } |
| 11053 | set env.terminated = true; |
| 11054 | } |
| 11055 | |
| 11056 | /// Find the local root borrowed or consumed by an argument expression. |
| 11057 | fn linearRootSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol { |
| 11058 | match node.value { |
| 11059 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => |
| 11060 | return symbolFor(self, node), |
| 11061 | case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value), |
| 11062 | case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target), |
| 11063 | case ast::NodeValue::FieldAccess(access) => |
| 11064 | return linearRootSymbol(self, access.parent), |
| 11065 | case ast::NodeValue::Subscript { container, .. } => |
| 11066 | return linearRootSymbol(self, container), |
| 11067 | case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target), |
| 11068 | else => return nil, |
| 11069 | } |
| 11070 | } |
| 11071 | |
| 11072 | /// A projection loan that remains active until its named region ends. |
| 11073 | record RegionalLoan: Copy { |
| 11074 | /// Address expression that supplies access to the loan. |
| 11075 | source: *ast::Node, |
| 11076 | /// Source identity of the projection's declared lifetime. |
| 11077 | regionId: u32, |
| 11078 | /// Storage protected by the projection. |
| 11079 | place: BorrowPlace, |
| 11080 | /// Whether accesses through other references are excluded. |
| 11081 | exclusive: bool, |
| 11082 | } |
| 11083 | |
| 11084 | /// Read an initialized regional loan from the active prefix. |
| 11085 | fn regionalLoan 'arena 'checking ( |
| 11086 | checker: &LinearChecker 'arena 'checking, index: u32 |
| 11087 | ) -> RegionalLoan where 'arena: 'checking { |
| 11088 | assert index < checker.regionalLen, "regionalLoan: index outside active prefix"; |
| 11089 | let loan = checker.regional[index] else panic "regionalLoan: missing active loan"; |
| 11090 | return loan; |
| 11091 | } |
| 11092 | |
| 11093 | /// Return whether a region identity is visible in a lexical environment. |
| 11094 | fn regionInScope(scope: ?*RegionScope, regionId: u32) -> bool { |
| 11095 | let mut cursor = scope; |
| 11096 | while let current = cursor { |
| 11097 | if regionIndex(current, regionId) <> nil { |
| 11098 | return true; |
| 11099 | } |
| 11100 | set cursor = current.parent; |
| 11101 | } |
| 11102 | return false; |
| 11103 | } |
| 11104 | |
| 11105 | /// Retain only loans whose regions remain active at a control-flow destination. |
| 11106 | fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking { |
| 11107 | let mut result: u64 = 0; |
| 11108 | for i in 0..checker.regionalLen { |
| 11109 | let bit = (1 as u64) << (i as u64); |
| 11110 | if (mask & bit) <> 0 and regionInScope(scope, regionalLoan(checker, i).regionId) { |
| 11111 | set result |= bit; |
| 11112 | } |
| 11113 | } |
| 11114 | return result; |
| 11115 | } |
| 11116 | |
| 11117 | /// Remap one loan mask after the regional loan table is compacted. |
| 11118 | fn remapRegionalLoans(mask: u64, mapping: &[u64]) -> u64 { |
| 11119 | let mut result: u64 = 0; |
| 11120 | for replacement, i in mapping { |
| 11121 | if (mask & ((1 as u64) << (i as u64))) <> 0 { |
| 11122 | set result |= replacement; |
| 11123 | } |
| 11124 | } |
| 11125 | return result; |
| 11126 | } |
| 11127 | |
| 11128 | /// Reclaim ended-region entries and preserve loans for enclosing regions. |
| 11129 | fn compactRegionalLoans 'arena 'checking ( |
| 11130 | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, |
| 11131 | scope: ?*RegionScope |
| 11132 | ) where 'arena: 'checking { |
| 11133 | let oldLen = checker.regionalLen; |
| 11134 | let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS]; |
| 11135 | let mut next: u32 = 0; |
| 11136 | for i in 0..oldLen { |
| 11137 | let loan = regionalLoan(checker, i); |
| 11138 | if regionInScope(scope, loan.regionId) { |
| 11139 | set checker.regional[next] = loan; |
| 11140 | set mapping[i] = (1 as u64) << (next as u64); |
| 11141 | set next += 1; |
| 11142 | } |
| 11143 | } |
| 11144 | set env.regionalLoans = remapRegionalLoans(env.regionalLoans, &mapping[..oldLen]); |
| 11145 | for i in 0..checker.loopDepth { |
| 11146 | set checker.loopBackLoans[i] = remapRegionalLoans(checker.loopBackLoans[i], &mapping[..oldLen]); |
| 11147 | set checker.loopExitLoans[i] = remapRegionalLoans(checker.loopExitLoans[i], &mapping[..oldLen]); |
| 11148 | } |
| 11149 | set checker.regionalLen = next; |
| 11150 | } |
| 11151 | |
| 11152 | /// Check whether an access comes from the reference created by a projection. |
| 11153 | unsafe fn usesRegionalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: *ast::Node) -> bool { |
| 11154 | if node.id == source.id { |
| 11155 | return true; |
| 11156 | } |
| 11157 | let root = linearRootSymbol(self, node) else return false; |
| 11158 | let origin = localReferenceSource(root) else return false; |
| 11159 | return usesRegionalLoan(self, origin, source); |
| 11160 | } |
| 11161 | |
| 11162 | /// Retain a full-region projection independently of its local binding scope. |
| 11163 | unsafe fn addRegionalLoan 'arena 'checking ( |
| 11164 | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node, address: ast::AddressOf |
| 11165 | ) throws (ResolveError) where 'arena: 'checking { |
| 11166 | let ty = typeFor(checker.resolver, node) else return; |
| 11167 | let mut class = types::PointerClass::Ref; |
| 11168 | match ty { |
| 11169 | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
| 11170 | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
| 11171 | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
| 11172 | else => return, |
| 11173 | } |
| 11174 | let case types::PointerClass::Region(region) = class else return; |
| 11175 | let storage = addressStorageClass(checker.resolver, address.target); |
| 11176 | let case types::PointerClass::Region(parent) = storage else return; |
| 11177 | if parent.id <> region.id { |
| 11178 | return; |
| 11179 | } |
| 11180 | let place = borrowPlace(checker.resolver, address.target); |
| 11181 | if place.root == nil { |
| 11182 | return; |
| 11183 | } |
| 11184 | for i in 0..checker.regionalLen { |
| 11185 | let loan = regionalLoan(checker, i); |
| 11186 | if loan.source.id == node.id { |
| 11187 | set env.regionalLoans |= (1 as u64) << (i as u64); |
| 11188 | return; |
| 11189 | } |
| 11190 | } |
| 11191 | if checker.regionalLen >= MAX_REGIONAL_LOANS { |
| 11192 | throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow); |
| 11193 | } |
| 11194 | let index = checker.regionalLen; |
| 11195 | set checker.regional[index] = RegionalLoan { source: node, regionId: region.id, place, exclusive: ast::isExclusiveAddress(address) }; |
| 11196 | set checker.regionalLen += 1; |
| 11197 | set env.regionalLoans |= (1 as u64) << (index as u64); |
| 11198 | } |
| 11199 | |
| 11200 | /// Return whether an initializer copies a shared reference with a named region. |
| 11201 | /// Address expressions and casts retain a loan on their source storage. |
| 11202 | fn copiesRegionalReference(ty: Type, value: *ast::Node) -> bool { |
| 11203 | if referenceRegion(ty) == nil or isMutablePointerLike(ty) { |
| 11204 | return false; |
| 11205 | } |
| 11206 | match value.value { |
| 11207 | case ast::NodeValue::AddressOf(_), ast::NodeValue::As(_) => return false, |
| 11208 | else => return true, |
| 11209 | } |
| 11210 | } |
| 11211 | |
| 11212 | /// Return the initializer that supplies a local reference's storage. |
| 11213 | fn localReferenceSource(sym: &Symbol) -> ?*ast::Node { |
| 11214 | let case SymbolData::Value { type: ty, .. } = sym.data else return nil; |
| 11215 | if let case Type::Session(_) = ty { |
| 11216 | if let case ast::NodeValue::RegionBinding(binding) = sym.node.value { |
| 11217 | return binding.value; |
| 11218 | } |
| 11219 | } |
| 11220 | if isRefType(ty) { |
| 11221 | if let case ast::NodeValue::Let(binding) = sym.node.value { |
| 11222 | if copiesRegionalReference(ty, binding.value) { |
| 11223 | return nil; |
| 11224 | } |
| 11225 | return binding.value; |
| 11226 | } |
| 11227 | if let case ast::NodeValue::RegionBinding(binding) = sym.node.value { |
| 11228 | return binding.value; |
| 11229 | } |
| 11230 | } |
| 11231 | return nil; |
| 11232 | } |
| 11233 | |
| 11234 | /// Resolve a place through reference locals without extending its storage lifetime. |
| 11235 | unsafe fn borrowPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> BorrowPlace { |
| 11236 | let mut place = emptyBorrowPlace(); |
| 11237 | match node.value { |
| 11238 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
| 11239 | let sym = symbolFor(self, node) else return place; |
| 11240 | if let source = localReferenceSource(sym) { |
| 11241 | let origin = borrowPlace(self, source); |
| 11242 | if origin.root <> nil { |
| 11243 | return origin; |
| 11244 | } |
| 11245 | } |
| 11246 | set place.root = sym; |
| 11247 | } |
| 11248 | case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target), |
| 11249 | case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value), |
| 11250 | case ast::NodeValue::FieldAccess(access) => { |
| 11251 | set place = borrowPlace(self, access.parent); |
| 11252 | if let ty = typeFor(self, access.parent) { |
| 11253 | if let case Type::Pointer { .. } = ty; not isRefType(ty) and place.len > 0 { |
| 11254 | set place.len = 0; |
| 11255 | set place.precise = false; |
| 11256 | } |
| 11257 | if let case Type::Nominal(NominalType::Record(_)) = autoDeref(ty); |
| 11258 | place.precise and place.len < MAX_BORROW_FIELDS |
| 11259 | { |
| 11260 | if let index = recordFieldIndexFor(self, access.child) { |
| 11261 | set place.fields[place.len] = index; |
| 11262 | set place.len += 1; |
| 11263 | return place; |
| 11264 | } |
| 11265 | } |
| 11266 | } |
| 11267 | set place.precise = false; |
| 11268 | } |
| 11269 | case ast::NodeValue::Subscript { container, .. } => { |
| 11270 | set place = borrowPlace(self, container); |
| 11271 | if let ty = typeFor(self, container) { |
| 11272 | if let case Type::Slice { class, .. } = autoDeref(ty); not types::isReference(class) { |
| 11273 | set place.len = 0; |
| 11274 | } |
| 11275 | } |
| 11276 | set place.precise = false; |
| 11277 | } |
| 11278 | case ast::NodeValue::Deref(target) => { |
| 11279 | set place = borrowPlace(self, target); |
| 11280 | if let ty = typeFor(self, target); not isRefType(ty) and place.len > 0 { |
| 11281 | set place.len = 0; |
| 11282 | set place.precise = false; |
| 11283 | } |
| 11284 | } |
| 11285 | else => {} |
| 11286 | } |
| 11287 | return place; |
| 11288 | } |
| 11289 | |
| 11290 | /// Two places overlap unless distinct inline fields prove separation. |
| 11291 | fn placesOverlap(left: &BorrowPlace, right: &BorrowPlace) -> bool { |
| 11292 | if left.root == nil or left.root <> right.root { |
| 11293 | return false; |
| 11294 | } |
| 11295 | let count = left.len if left.len < right.len else right.len; |
| 11296 | for i in 0..count { |
| 11297 | if left.fields[i] <> right.fields[i] { |
| 11298 | return false; |
| 11299 | } |
| 11300 | } |
| 11301 | return true; |
| 11302 | } |
| 11303 | |
| 11304 | /// Check whether access uses a reference or one of its lexical reborrows. |
| 11305 | unsafe fn usesLocalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, binding: *unsafe mut Symbol) -> bool { |
| 11306 | let root = linearRootSymbol(self, node) else return false; |
| 11307 | if root == binding { |
| 11308 | return true; |
| 11309 | } |
| 11310 | let source = localReferenceSource(root) else return false; |
| 11311 | return usesLocalLoan(self, source, binding); |
| 11312 | } |
| 11313 | /// Reject accesses that conflict with a reference in an active lexical scope. |
| 11314 | unsafe fn checkLocalLoans 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, exclusive: bool) |
| 11315 | throws (ResolveError) where 'arena: 'checking |
| 11316 | { |
| 11317 | let place = borrowPlace(checker.resolver, node); |
| 11318 | let root = place.root; |
| 11319 | if let placeRoot = root { |
| 11320 | for i in 0..checker.regionalLen { |
| 11321 | if (env.regionalLoans & ((1 as u64) << (i as u64))) == 0 { |
| 11322 | continue; |
| 11323 | } |
| 11324 | let loan = regionalLoan(checker, i); |
| 11325 | if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place) |
| 11326 | and not usesRegionalLoan(checker.resolver, node, loan.source) |
| 11327 | { |
| 11328 | throw emitError( |
| 11329 | checker.resolver, node, ErrorKind::BorrowConflict(placeRoot.name), |
| 11330 | ); |
| 11331 | } |
| 11332 | } |
| 11333 | } |
| 11334 | let mut accessRegion: ?*unsafe types::Region = nil; |
| 11335 | if let ty = typeFor(checker.resolver, node) { |
| 11336 | set accessRegion = referenceRegion(ty); |
| 11337 | } |
| 11338 | if let cellTy = try explicitCellPayload(checker.resolver, node) { |
| 11339 | let case Type::Cell { permission, .. } = cellTy |
| 11340 | else panic "checkLocalLoans: invalid cell payload"; |
| 11341 | set accessRegion = permission; |
| 11342 | } else if let cellTy = try inferCellPayload(checker.resolver, node) { |
| 11343 | let case Type::Cell { permission, .. } = cellTy |
| 11344 | else panic "checkLocalLoans: invalid cell payload"; |
| 11345 | set accessRegion = permission; |
| 11346 | } |
| 11347 | for i in 0..checker.localLen { |
| 11348 | let loan = checker.locals[i]; |
| 11349 | let mut throughBinding = false; |
| 11350 | if let binding = loan.binding { |
| 11351 | set throughBinding = usesLocalLoan(checker.resolver, node, binding); |
| 11352 | } |
| 11353 | if let placeRoot = root; |
| 11354 | (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place) |
| 11355 | and not throughBinding |
| 11356 | { |
| 11357 | throw emitError( |
| 11358 | checker.resolver, node, ErrorKind::BorrowConflict(placeRoot.name), |
| 11359 | ); |
| 11360 | } |
| 11361 | let permission = loan.permission else continue; |
| 11362 | let region = accessRegion else continue; |
| 11363 | let mut identityMatches = permission.id == region.id; |
| 11364 | if let storage = loan.storage; storage.id == region.id { |
| 11365 | set identityMatches = true; |
| 11366 | } |
| 11367 | if not identityMatches or not (exclusive or loan.exclusive) or throughBinding { |
| 11368 | continue; |
| 11369 | } |
| 11370 | let mut name = permission.name; |
| 11371 | if let placeRoot = root { |
| 11372 | set name = placeRoot.name; |
| 11373 | } |
| 11374 | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(name)); |
| 11375 | } |
| 11376 | } |
| 11377 | |
| 11378 | /// Return cell metadata for an explicit payload borrow through transparent wrappers. |
| 11379 | unsafe fn explicitCellPayload 'arena ( |
| 11380 | self: &mut Resolver 'arena, node: *ast::Node |
| 11381 | ) -> ?Type throws (ResolveError) { |
| 11382 | match node.value { |
| 11383 | case ast::NodeValue::AddressOf(address) => |
| 11384 | return try inferCellPayload(self, address.target), |
| 11385 | case ast::NodeValue::As(cast) => |
| 11386 | return try explicitCellPayload(self, cast.value), |
| 11387 | case ast::NodeValue::RegionApply { value, .. } => |
| 11388 | return try explicitCellPayload(self, value), |
| 11389 | else => return nil, |
| 11390 | } |
| 11391 | } |
| 11392 | |
| 11393 | /// Retain source storage for local borrows and region headers. |
| 11394 | unsafe fn addLocalLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, binding: ast::Let) |
| 11395 | throws (ResolveError) where 'arena: 'checking |
| 11396 | { |
| 11397 | let ty = typeFor(checker.resolver, binding.ident) else return; |
| 11398 | if not isRefType(ty) { |
| 11399 | let case Type::Session(_) = ty else return; |
| 11400 | let case ast::NodeValue::RegionBinding(_) = node.value else return; |
| 11401 | } |
| 11402 | if let case ast::NodeValue::Let(_) = node.value; |
| 11403 | copiesRegionalReference(ty, binding.value) |
| 11404 | { |
| 11405 | return; |
| 11406 | } |
| 11407 | let place = borrowPlace(checker.resolver, binding.value); |
| 11408 | if place.root == nil { |
| 11409 | if referenceRegion(ty) <> nil { |
| 11410 | return; |
| 11411 | } |
| 11412 | throw emitError(checker.resolver, node, ErrorKind::RefBinding); |
| 11413 | } |
| 11414 | if checker.localLen >= MAX_LINEAR_BINDINGS { |
| 11415 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
| 11416 | } |
| 11417 | let sym = symbolFor(checker.resolver, node) else panic "reference without binding"; |
| 11418 | let mut exclusive = isExclusiveArgument(ty) or createsCellBorrow(binding.value); |
| 11419 | if let case Type::Cell { .. } = ty { |
| 11420 | if let case ast::NodeValue::As(expr) = binding.value.value { |
| 11421 | if let source = typeFor(checker.resolver, expr.value) { |
| 11422 | if let case Type::Pointer { mutable: true, .. } = source { |
| 11423 | set exclusive = true; |
| 11424 | } |
| 11425 | } |
| 11426 | } |
| 11427 | } |
| 11428 | try checkLocalLoans(checker, env, binding.value, exclusive); |
| 11429 | let mut permission: ?*unsafe types::Region = nil; |
| 11430 | let mut storage: ?*unsafe types::Region = nil; |
| 11431 | if let cellTy = try explicitCellPayload(checker.resolver, binding.value) { |
| 11432 | let case Type::Cell { |
| 11433 | class, permission: cellPermission, .. |
| 11434 | } = cellTy else panic "addLocalLoan: invalid cell payload"; |
| 11435 | set permission = cellPermission; |
| 11436 | if let case types::PointerClass::Region(region) = class { |
| 11437 | set storage = region; |
| 11438 | } |
| 11439 | } |
| 11440 | set checker.locals[checker.localLen] = LocalLoan { |
| 11441 | binding: sym, place, exclusive, permission, storage, |
| 11442 | }; |
| 11443 | set checker.localLen += 1; |
| 11444 | } |
| 11445 | |
| 11446 | /// Protect storage borrowed by a pointer pattern until its bindings leave scope. |
| 11447 | unsafe fn addPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, subject: *ast::Node) |
| 11448 | throws (ResolveError) where 'arena: 'checking |
| 11449 | { |
| 11450 | let ty = typeFor(checker.resolver, subject) else return; |
| 11451 | if unwrapMatchSubject(ty).by == MatchBy::Value { |
| 11452 | return; |
| 11453 | } |
| 11454 | let place = borrowPlace(checker.resolver, subject); |
| 11455 | if place.root == nil { |
| 11456 | return; |
| 11457 | } |
| 11458 | if checker.loanLen >= MAX_LINEAR_BINDINGS { |
| 11459 | throw emitError(checker.resolver, subject, ErrorKind::Internal); |
| 11460 | } |
| 11461 | set checker.loans[checker.loanLen] = place; |
| 11462 | set checker.loanLen += 1; |
| 11463 | } |
| 11464 | |
| 11465 | /// Reject a write, mutable loan, or ownership transfer of a pattern source. |
| 11466 | unsafe fn checkPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, node: *ast::Node) |
| 11467 | throws (ResolveError) where 'arena: 'checking |
| 11468 | { |
| 11469 | let place = borrowPlace(checker.resolver, node); |
| 11470 | let root = place.root else return; |
| 11471 | for i in 0..checker.loanLen { |
| 11472 | if placesOverlap(&checker.loans[i], &place) { |
| 11473 | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
| 11474 | } |
| 11475 | } |
| 11476 | } |
| 11477 | |
| 11478 | /// Return whether a parameter borrows its argument only for the call. |
| 11479 | fn isBorrowedReferenceParameter(ty: Type) -> bool { |
| 11480 | if not isRefType(ty) { |
| 11481 | return false; |
| 11482 | } |
| 11483 | match ty { |
| 11484 | case Type::Cell { class, .. } => return class == types::PointerClass::Ref, |
| 11485 | case Type::Pointer { class, .. } => return class == types::PointerClass::Ref, |
| 11486 | case Type::Slice { class, .. } => return class == types::PointerClass::Ref, |
| 11487 | case Type::TraitObject { class, .. } => return class == types::PointerClass::Ref, |
| 11488 | else => return false, |
| 11489 | } |
| 11490 | } |
| 11491 | |
| 11492 | /// Return whether a type carries one region in the requested compile-time role. |
| 11493 | /// Applied nominal visits are generation- and role-marked; unapplied nominals |
| 11494 | /// cannot carry the queried free region and require no member traversal. |
| 11495 | unsafe fn typeHasRegionRole 'arena ( |
| 11496 | self: &mut Resolver 'arena, |
| 11497 | ty: Type, |
| 11498 | region: *unsafe types::Region, |
| 11499 | role: RegionTypeRole, |
| 11500 | generation: u32, |
| 11501 | ) -> bool { |
| 11502 | if role == RegionTypeRole::Reference { |
| 11503 | if let dependency = referenceRegion(ty); dependency == region { |
| 11504 | return true; |
| 11505 | } |
| 11506 | } |
| 11507 | match ty { |
| 11508 | case Type::Cell { permission, payload, .. } => { |
| 11509 | if role == RegionTypeRole::Retention { |
| 11510 | return typeHasRegionRole( |
| 11511 | self, *payload, region, RegionTypeRole::Reference, generation, |
| 11512 | ) or typeHasRegionRole( |
| 11513 | self, *payload, region, RegionTypeRole::Retention, generation, |
| 11514 | ); |
| 11515 | } |
| 11516 | if role == RegionTypeRole::CellPermission { |
| 11517 | if let identity = permission; identity == region { |
| 11518 | return true; |
| 11519 | } |
| 11520 | } |
| 11521 | return typeHasRegionRole( |
| 11522 | self, *payload, region, role, generation, |
| 11523 | ); |
| 11524 | } |
| 11525 | case Type::Session(identity) => |
| 11526 | return role == RegionTypeRole::Retention |
| 11527 | and types::regionContains(region, identity), |
| 11528 | case Type::Pointer { target, .. } => |
| 11529 | return typeHasRegionRole( |
| 11530 | self, *target, region, role, generation, |
| 11531 | ), |
| 11532 | case Type::Slice { item, .. } => |
| 11533 | return typeHasRegionRole( |
| 11534 | self, *item, region, role, generation, |
| 11535 | ), |
| 11536 | case Type::Array(array) => |
| 11537 | return typeHasRegionRole( |
| 11538 | self, *array.item, region, role, generation, |
| 11539 | ), |
| 11540 | case Type::Optional(inner) => |
| 11541 | return typeHasRegionRole( |
| 11542 | self, *inner, region, role, generation, |
| 11543 | ), |
| 11544 | case Type::Range { start, end } => { |
| 11545 | if let startType = start; |
| 11546 | typeHasRegionRole( |
| 11547 | self, *startType, region, role, generation, |
| 11548 | ) |
| 11549 | { |
| 11550 | return true; |
| 11551 | } |
| 11552 | if let endType = end { |
| 11553 | return typeHasRegionRole( |
| 11554 | self, *endType, region, role, generation, |
| 11555 | ); |
| 11556 | } |
| 11557 | return false; |
| 11558 | } |
| 11559 | case Type::Nominal(info) => { |
| 11560 | let applied = nominalApplication(info) else return false; |
| 11561 | if not visitNominalApplication(applied, generation, role) { |
| 11562 | // This application and role have no unvisited members. |
| 11563 | return false; |
| 11564 | } |
| 11565 | match *info { |
| 11566 | case NominalType::Record(recordType) => { |
| 11567 | for field in recordType.fields { |
| 11568 | if typeHasRegionRole( |
| 11569 | self, field.fieldType, region, role, generation, |
| 11570 | ) { |
| 11571 | return true; |
| 11572 | } |
| 11573 | } |
| 11574 | } |
| 11575 | case NominalType::Union(unionType) => { |
| 11576 | for variant in unionType.variants { |
| 11577 | if typeHasRegionRole( |
| 11578 | self, variant.valueType, region, role, generation, |
| 11579 | ) { |
| 11580 | return true; |
| 11581 | } |
| 11582 | } |
| 11583 | } |
| 11584 | // Linear checking normally sees completed applications. Deny |
| 11585 | // reuse without treating an unknown view as permission proof. |
| 11586 | case NominalType::Placeholder(_), NominalType::Resolving(_), |
| 11587 | NominalType::Application(_) => |
| 11588 | return role <> RegionTypeRole::CellPermission, |
| 11589 | } |
| 11590 | return false; |
| 11591 | } |
| 11592 | case Type::Fn(info) => { |
| 11593 | for parameter in info.paramTypes { |
| 11594 | if typeHasRegionRole( |
| 11595 | self, *parameter, region, role, generation, |
| 11596 | ) { |
| 11597 | return true; |
| 11598 | } |
| 11599 | } |
| 11600 | for error in info.throwList { |
| 11601 | if typeHasRegionRole( |
| 11602 | self, *error, region, role, generation, |
| 11603 | ) { |
| 11604 | return true; |
| 11605 | } |
| 11606 | } |
| 11607 | return typeHasRegionRole( |
| 11608 | self, *info.returnType, region, role, generation, |
| 11609 | ); |
| 11610 | } |
| 11611 | else => return false, |
| 11612 | } |
| 11613 | } |
| 11614 | |
| 11615 | /// Classify one call argument from the instantiated function contract. |
| 11616 | /// Regional mutable authority is call-borrowed only when the contract uses its |
| 11617 | /// region as cell permission and no result, error, other input, or mutable |
| 11618 | /// receiver can retain a reference carrying that region. |
| 11619 | unsafe fn callParameterUse 'arena ( |
| 11620 | self: &mut Resolver 'arena, |
| 11621 | info: *FnType, |
| 11622 | parameterIndex: u32, |
| 11623 | receiver: ?Type, |
| 11624 | receiverMutable: bool, |
| 11625 | ) -> LinearUse { |
| 11626 | let parameter = *info.paramTypes[parameterIndex]; |
| 11627 | if isBorrowedReferenceParameter(parameter) { |
| 11628 | return LinearUse::Borrow; |
| 11629 | } |
| 11630 | let case Type::Pointer { |
| 11631 | class: types::PointerClass::Region(region), mutable: true, .. |
| 11632 | } = parameter else return LinearUse::Consume; |
| 11633 | |
| 11634 | let permissionGeneration = nextNominalTraversalGeneration(self); |
| 11635 | let mut hasPermission = false; |
| 11636 | if let receiverType = receiver { |
| 11637 | set hasPermission = typeHasRegionRole( |
| 11638 | self, receiverType, region, RegionTypeRole::CellPermission, |
| 11639 | permissionGeneration, |
| 11640 | ); |
| 11641 | } |
| 11642 | if not hasPermission { |
| 11643 | for candidate in info.paramTypes { |
| 11644 | if typeHasRegionRole( |
| 11645 | self, *candidate, region, RegionTypeRole::CellPermission, |
| 11646 | permissionGeneration, |
| 11647 | ) { |
| 11648 | set hasPermission = true; |
| 11649 | break; |
| 11650 | } |
| 11651 | } |
| 11652 | } |
| 11653 | if not hasPermission { |
| 11654 | for error in info.throwList { |
| 11655 | if typeHasRegionRole( |
| 11656 | self, *error, region, RegionTypeRole::CellPermission, |
| 11657 | permissionGeneration, |
| 11658 | ) { |
| 11659 | set hasPermission = true; |
| 11660 | break; |
| 11661 | } |
| 11662 | } |
| 11663 | } |
| 11664 | if not hasPermission { |
| 11665 | set hasPermission = typeHasRegionRole( |
| 11666 | self, *info.returnType, region, RegionTypeRole::CellPermission, |
| 11667 | permissionGeneration, |
| 11668 | ); |
| 11669 | } |
| 11670 | if not hasPermission { |
| 11671 | return LinearUse::Consume; |
| 11672 | } |
| 11673 | |
| 11674 | // Results and thrown values outlive the call directly. |
| 11675 | let resultGeneration = nextNominalTraversalGeneration(self); |
| 11676 | if typeHasRegionRole( |
| 11677 | self, *info.returnType, region, RegionTypeRole::Reference, |
| 11678 | resultGeneration, |
| 11679 | ) { |
| 11680 | return LinearUse::Consume; |
| 11681 | } |
| 11682 | for error in info.throwList { |
| 11683 | if typeHasRegionRole( |
| 11684 | self, *error, region, RegionTypeRole::Reference, resultGeneration, |
| 11685 | ) { |
| 11686 | return LinearUse::Consume; |
| 11687 | } |
| 11688 | } |
| 11689 | |
| 11690 | // A mutable input can store the authority reference, while cells and |
| 11691 | // sessions provide interior retention even through an otherwise shared |
| 11692 | // contract position. Exclude the authority parameter itself. |
| 11693 | for candidate, i in info.paramTypes { |
| 11694 | if i == parameterIndex { |
| 11695 | continue; |
| 11696 | } |
| 11697 | let retentionGeneration = nextNominalTraversalGeneration(self); |
| 11698 | if typeHasRegionRole( |
| 11699 | self, *candidate, region, RegionTypeRole::Retention, |
| 11700 | retentionGeneration, |
| 11701 | ) { |
| 11702 | return LinearUse::Consume; |
| 11703 | } |
| 11704 | let referenceGeneration = nextNominalTraversalGeneration(self); |
| 11705 | if typeHasRegionRole( |
| 11706 | self, *candidate, region, RegionTypeRole::Reference, |
| 11707 | referenceGeneration, |
| 11708 | ) and isExclusiveArgument(*candidate) { |
| 11709 | return LinearUse::Consume; |
| 11710 | } |
| 11711 | } |
| 11712 | |
| 11713 | if let receiverType = receiver { |
| 11714 | let retentionGeneration = nextNominalTraversalGeneration(self); |
| 11715 | if typeHasRegionRole( |
| 11716 | self, receiverType, region, RegionTypeRole::Retention, |
| 11717 | retentionGeneration, |
| 11718 | ) { |
| 11719 | return LinearUse::Consume; |
| 11720 | } |
| 11721 | if receiverMutable { |
| 11722 | let referenceGeneration = nextNominalTraversalGeneration(self); |
| 11723 | if typeHasRegionRole( |
| 11724 | self, receiverType, region, RegionTypeRole::Reference, |
| 11725 | referenceGeneration, |
| 11726 | ) { |
| 11727 | return LinearUse::Consume; |
| 11728 | } |
| 11729 | } |
| 11730 | } |
| 11731 | return LinearUse::Borrow; |
| 11732 | } |
| 11733 | |
| 11734 | /// Return whether a parameter can mutate or consume its argument's storage. |
| 11735 | unsafe fn isExclusiveArgument(ty: Type) -> bool { |
| 11736 | match ty { |
| 11737 | case Type::Pointer { mutable, .. } => return mutable, |
| 11738 | case Type::Slice { mutable, .. } => return mutable, |
| 11739 | case Type::TraitObject { mutable, .. } => return mutable, |
| 11740 | else => return isMoveOnly(ty), |
| 11741 | } |
| 11742 | } |
| 11743 | |
| 11744 | /// Add the value identifiers introduced by a pattern. |
| 11745 | /// Return whether the pattern introduces references to its source storage. |
| 11746 | unsafe fn addLinearPatternBindings 'arena 'checking ( |
| 11747 | checker: &mut LinearChecker 'arena 'checking, |
| 11748 | env: &mut LinearEnv, |
| 11749 | pattern: *ast::Node, |
| 11750 | ) -> bool throws (ResolveError) where 'arena: 'checking { |
| 11751 | let mut hasReferences = false; |
| 11752 | match pattern.value { |
| 11753 | case ast::NodeValue::Ident(_) => { |
| 11754 | try addLinearBinding(checker, env, pattern); |
| 11755 | if let ty = typeFor(checker.resolver, pattern) { |
| 11756 | return isRefType(ty); |
| 11757 | } |
| 11758 | } |
| 11759 | case ast::NodeValue::Call(call) => { |
| 11760 | for arg in call.args { |
| 11761 | if try addLinearPatternBindings(checker, env, arg) { |
| 11762 | set hasReferences = true; |
| 11763 | } |
| 11764 | } |
| 11765 | } |
| 11766 | case ast::NodeValue::RecordLit(lit) => { |
| 11767 | for fieldNode in lit.fields { |
| 11768 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value |
| 11769 | else panic "addLinearPatternBindings: expected field"; |
| 11770 | if try addLinearPatternBindings(checker, env, field.value) { |
| 11771 | set hasReferences = true; |
| 11772 | } |
| 11773 | } |
| 11774 | } |
| 11775 | case ast::NodeValue::ArrayLit(items) => { |
| 11776 | for item in items { |
| 11777 | if try addLinearPatternBindings(checker, env, item) { |
| 11778 | set hasReferences = true; |
| 11779 | } |
| 11780 | } |
| 11781 | } |
| 11782 | else => {} |
| 11783 | } |
| 11784 | return hasReferences; |
| 11785 | } |
| 11786 | |
| 11787 | /// Check a lexical block and exact-use of locals introduced in it. |
| 11788 | unsafe fn checkLinearBlock 'arena 'checking ( |
| 11789 | checker: &mut LinearChecker 'arena 'checking, |
| 11790 | env: &mut LinearEnv, |
| 11791 | node: *ast::Node, |
| 11792 | ) throws (ResolveError) where 'arena: 'checking { |
| 11793 | let start = env.len; |
| 11794 | let localStart = checker.localLen; |
| 11795 | let authorityStart = checker.authorityLen; |
| 11796 | let case ast::NodeValue::Block(block) = node.value |
| 11797 | else panic "checkLinearBlock: expected block"; |
| 11798 | for stmt in block.statements { |
| 11799 | if env.terminated { |
| 11800 | break; |
| 11801 | } |
| 11802 | try checkLinearNode(checker, env, stmt, LinearUse::Discard); |
| 11803 | } |
| 11804 | try finishLinearScope(checker, env, start); |
| 11805 | set checker.localLen = localStart; |
| 11806 | set checker.authorityLen = authorityStart; |
| 11807 | } |
| 11808 | |
| 11809 | /// Push a repeated-control-flow boundary. |
| 11810 | /// Initialize all loop state at this depth before increasing `loopDepth`. |
| 11811 | fn enterLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
| 11812 | assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow"; |
| 11813 | let depth = checker.loopDepth; |
| 11814 | set checker.loopBackLoans[depth] = 0; |
| 11815 | set checker.loopExitLoans[depth] = 0; |
| 11816 | set checker.loopRegions[depth] = checker.regions; |
| 11817 | set checker.loopMarks[depth] = env.len; |
| 11818 | set checker.loopAvailable[depth] = env.available; |
| 11819 | set checker.loopExitAvailable[depth] = env.available; |
| 11820 | set checker.loopHasNaturalExit[depth] = false; |
| 11821 | set checker.loopBreakSeen[depth] = false; |
| 11822 | set checker.loopDepth += 1; |
| 11823 | } |
| 11824 | |
| 11825 | /// Require a repeated body's outer bindings to match its entry state. |
| 11826 | fn checkLinearLoopBackEdge 'arena 'checking ( |
| 11827 | checker: &mut LinearChecker 'arena 'checking, |
| 11828 | env: &LinearEnv, |
| 11829 | node: *ast::Node, |
| 11830 | ) throws (ResolveError) where 'arena: 'checking { |
| 11831 | if env.terminated { |
| 11832 | return; |
| 11833 | } |
| 11834 | assert checker.loopDepth > 0, "linear loop back edge outside loop"; |
| 11835 | let depth = checker.loopDepth - 1; |
| 11836 | set checker.loopBackLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
| 11837 | let mark = checker.loopMarks[depth]; |
| 11838 | let entryAvailable = checker.loopAvailable[depth]; |
| 11839 | for i in 0..mark { |
| 11840 | let bit = (1 as u64) << (i as u64); |
| 11841 | if (env.available & bit) <> (entryAvailable & bit) { |
| 11842 | let sym = linearSymbol(env, i); |
| 11843 | throw emitError( |
| 11844 | checker.resolver, |
| 11845 | node, |
| 11846 | ErrorKind::LinearBranchMismatch(sym.name), |
| 11847 | ); |
| 11848 | } |
| 11849 | } |
| 11850 | } |
| 11851 | |
| 11852 | /// Record the ownership state of a loop's condition-false exit. |
| 11853 | fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
| 11854 | assert checker.loopDepth > 0, "linear loop exit outside loop"; |
| 11855 | let depth = checker.loopDepth - 1; |
| 11856 | set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
| 11857 | set checker.loopExitAvailable[depth] = env.available; |
| 11858 | set checker.loopHasNaturalExit[depth] = true; |
| 11859 | } |
| 11860 | |
| 11861 | /// Require a break exit to agree with every other exit from this loop. |
| 11862 | fn checkLinearLoopBreak 'arena 'checking ( |
| 11863 | checker: &mut LinearChecker 'arena 'checking, |
| 11864 | env: &LinearEnv, |
| 11865 | node: *ast::Node, |
| 11866 | ) throws (ResolveError) where 'arena: 'checking { |
| 11867 | assert checker.loopDepth > 0, "linear loop break outside loop"; |
| 11868 | let depth = checker.loopDepth - 1; |
| 11869 | set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
| 11870 | let mark = checker.loopMarks[depth]; |
| 11871 | if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] { |
| 11872 | let expected = checker.loopExitAvailable[depth]; |
| 11873 | for i in 0..mark { |
| 11874 | let bit = (1 as u64) << (i as u64); |
| 11875 | if (env.available & bit) <> (expected & bit) { |
| 11876 | let sym = linearSymbol(env, i); |
| 11877 | throw emitError( |
| 11878 | checker.resolver, |
| 11879 | node, |
| 11880 | ErrorKind::LinearBranchMismatch(sym.name), |
| 11881 | ); |
| 11882 | } |
| 11883 | } |
| 11884 | } else { |
| 11885 | set checker.loopExitAvailable[depth] = env.available; |
| 11886 | } |
| 11887 | set checker.loopBreakSeen[depth] = true; |
| 11888 | } |
| 11889 | |
| 11890 | /// Pop a repeated-control-flow boundary. |
| 11891 | fn exitLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking) where 'arena: 'checking { |
| 11892 | assert checker.loopDepth > 0, "exitLinearLoop: not in loop"; |
| 11893 | set checker.loopDepth -= 1; |
| 11894 | } |
| 11895 | |
| 11896 | /// Check a conditional and merge its ownership states. |
| 11897 | unsafe fn checkLinearIf 'arena 'checking ( |
| 11898 | checker: &mut LinearChecker 'arena 'checking, |
| 11899 | env: &mut LinearEnv, |
| 11900 | node: *ast::Node, |
| 11901 | conditional: ast::If, |
| 11902 | ) throws (ResolveError) where 'arena: 'checking { |
| 11903 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
| 11904 | let base = *env; |
| 11905 | let mut thenEnv = base; |
| 11906 | try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard); |
| 11907 | let mut elseEnv = base; |
| 11908 | if let branch = conditional.elseBranch { |
| 11909 | try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard); |
| 11910 | } |
| 11911 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
| 11912 | } |
| 11913 | |
| 11914 | /// Check an expression conditional and merge its ownership states. |
| 11915 | unsafe fn checkLinearCondExpr 'arena 'checking ( |
| 11916 | checker: &mut LinearChecker 'arena 'checking, |
| 11917 | env: &mut LinearEnv, |
| 11918 | node: *ast::Node, |
| 11919 | conditional: ast::CondExpr, |
| 11920 | usage: LinearUse, |
| 11921 | ) throws (ResolveError) where 'arena: 'checking { |
| 11922 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
| 11923 | let base = *env; |
| 11924 | let mut thenEnv = base; |
| 11925 | try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage); |
| 11926 | let mut elseEnv = base; |
| 11927 | try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage); |
| 11928 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
| 11929 | } |
| 11930 | |
| 11931 | /// Pointer patterns borrow their subject; value patterns consume it. |
| 11932 | fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse { |
| 11933 | if let ty = typeFor(self, subject) { |
| 11934 | if let case Type::Pointer { .. } = ty { |
| 11935 | return LinearUse::Borrow; |
| 11936 | } |
| 11937 | } |
| 11938 | return LinearUse::Consume; |
| 11939 | } |
| 11940 | |
| 11941 | /// Check a match expression, including ownership transferred into patterns. |
| 11942 | unsafe fn checkLinearMatch 'arena 'checking ( |
| 11943 | checker: &mut LinearChecker 'arena 'checking, |
| 11944 | env: &mut LinearEnv, |
| 11945 | node: *ast::Node, |
| 11946 | matchExpr: ast::Match, |
| 11947 | ) throws (ResolveError) where 'arena: 'checking { |
| 11948 | try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject)); |
| 11949 | let base = *env; |
| 11950 | let mut haveResult = false; |
| 11951 | let mut result = base; |
| 11952 | for prongNode in matchExpr.prongs { |
| 11953 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
| 11954 | else panic "checkLinearMatch: expected prong"; |
| 11955 | let mut branch = base; |
| 11956 | let bindingsStart = branch.len; |
| 11957 | let loanStart = checker.loanLen; |
| 11958 | match prong.arm { |
| 11959 | case ast::ProngArm::Case(patterns) => { |
| 11960 | for pattern in patterns { |
| 11961 | if try addLinearPatternBindings(checker, &mut branch, pattern) { |
| 11962 | try addPatternLoan(checker, matchExpr.subject); |
| 11963 | } |
| 11964 | } |
| 11965 | } |
| 11966 | case ast::ProngArm::Binding(binding) => { |
| 11967 | if try addLinearPatternBindings(checker, &mut branch, binding) { |
| 11968 | try addPatternLoan(checker, matchExpr.subject); |
| 11969 | } |
| 11970 | } |
| 11971 | case ast::ProngArm::Else => {} |
| 11972 | } |
| 11973 | if prong.guard <> nil { |
| 11974 | for i in bindingsStart..branch.len { |
| 11975 | let sym = linearSymbol(&branch, i); |
| 11976 | if sym.usage == BindingUse::Linear { |
| 11977 | throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard); |
| 11978 | } |
| 11979 | } |
| 11980 | } |
| 11981 | if let guard = prong.guard { |
| 11982 | try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume); |
| 11983 | } |
| 11984 | try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard); |
| 11985 | try finishLinearScope(checker, &mut branch, bindingsStart); |
| 11986 | set checker.loanLen = loanStart; |
| 11987 | if haveResult { |
| 11988 | let previous = result; |
| 11989 | try joinLinearBranches(checker, &mut result, &previous, &branch, node); |
| 11990 | } else { |
| 11991 | set result = branch; |
| 11992 | set haveResult = true; |
| 11993 | } |
| 11994 | } |
| 11995 | if haveResult { |
| 11996 | set *env = result; |
| 11997 | } |
| 11998 | } |
| 11999 | |
| 12000 | /// Check call-scoped loans and argument ownership transfers. |
| 12001 | unsafe fn checkLinearCall 'arena 'checking ( |
| 12002 | checker: &mut LinearChecker 'arena 'checking, |
| 12003 | env: &mut LinearEnv, |
| 12004 | node: *ast::Node, |
| 12005 | call: ast::Call, |
| 12006 | ) throws (ResolveError) where 'arena: 'checking { |
| 12007 | let localStart = checker.localLen; |
| 12008 | match checker.resolver.nodeData.entries[node.id].extra { |
| 12009 | case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => { |
| 12010 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
| 12011 | else panic "slice mutation without receiver"; |
| 12012 | try checkPatternLoan(checker, access.parent); |
| 12013 | try checkLocalLoans(checker, env, access.parent, true); |
| 12014 | } |
| 12015 | else => {} |
| 12016 | } |
| 12017 | try checkLinearNode(checker, env, call.callee, LinearUse::Observe); |
| 12018 | let mut fnInfo: ?*FnType = nil; |
| 12019 | if let calleeTy = typeFor(checker.resolver, call.callee) { |
| 12020 | if let case Type::Fn(info) = calleeTy { |
| 12021 | set fnInfo = info; |
| 12022 | } |
| 12023 | } |
| 12024 | if fnInfo == nil { |
| 12025 | match checker.resolver.nodeData.entries[node.id].extra { |
| 12026 | case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => |
| 12027 | set fnInfo = traitInfo.methods[methodIndex].fnType, |
| 12028 | case NodeExtra::MethodCall { method } => set fnInfo = method.fnType, |
| 12029 | else => {} |
| 12030 | } |
| 12031 | } |
| 12032 | let info = fnInfo else { |
| 12033 | for arg in call.args { |
| 12034 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
| 12035 | } |
| 12036 | return; |
| 12037 | }; |
| 12038 | let mut arguments: [?CallArgument; MAX_FN_PARAMS + 1] = [nil; MAX_FN_PARAMS + 1]; |
| 12039 | let mut argumentsLen: u32 = 0; |
| 12040 | let mut contractReceiver: ?Type = nil; |
| 12041 | let mut contractReceiverMutable = false; |
| 12042 | |
| 12043 | // Method function types exclude their implicit receiver. Account for it |
| 12044 | // explicitly so owning receivers are consumed and reference receivers |
| 12045 | // participate in call-scoped loan conflict checks. |
| 12046 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
| 12047 | let mut receiverClass = types::PointerClass::Unsafe; |
| 12048 | let mut receiverMutable = false; |
| 12049 | let mut haveReceiver = false; |
| 12050 | match checker.resolver.nodeData.entries[node.id].extra { |
| 12051 | case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => { |
| 12052 | let method = &traitInfo.methods[methodIndex]; |
| 12053 | set receiverClass = method.receiverClass; |
| 12054 | set receiverMutable = method.mutable; |
| 12055 | set haveReceiver = true; |
| 12056 | } |
| 12057 | case NodeExtra::MethodCall { method } => { |
| 12058 | set receiverClass = method.receiverClass; |
| 12059 | set receiverMutable = method.mutable; |
| 12060 | set haveReceiver = true; |
| 12061 | } |
| 12062 | else => {} |
| 12063 | } |
| 12064 | if haveReceiver { |
| 12065 | if let receiverType = typeFor(checker.resolver, access.parent) { |
| 12066 | set contractReceiver = autoDeref(receiverType); |
| 12067 | } |
| 12068 | set contractReceiverMutable = receiverMutable; |
| 12069 | try checkLocalLoans(checker, env, access.parent, |
| 12070 | receiverMutable or receiverClass == types::PointerClass::Owned); |
| 12071 | if receiverMutable or receiverClass == types::PointerClass::Owned { |
| 12072 | try checkPatternLoan(checker, access.parent); |
| 12073 | } |
| 12074 | if receiverClass <> types::PointerClass::Unsafe { |
| 12075 | set arguments[argumentsLen] = CallArgument { |
| 12076 | node: access.parent, |
| 12077 | exclusive: receiverClass == types::PointerClass::Owned or receiverMutable, |
| 12078 | }; |
| 12079 | set argumentsLen += 1; |
| 12080 | } |
| 12081 | if types::isReference(receiverClass) { |
| 12082 | try checkLinearNode(checker, env, access.parent, LinearUse::Borrow); |
| 12083 | if createsExplicitBorrow(access.parent) { |
| 12084 | try retainCallLoan(checker, access.parent, receiverMutable); |
| 12085 | } |
| 12086 | } else if receiverClass == types::PointerClass::Owned { |
| 12087 | try checkLinearNode(checker, env, access.parent, LinearUse::Consume); |
| 12088 | } |
| 12089 | } |
| 12090 | } |
| 12091 | |
| 12092 | for arg, i in call.args { |
| 12093 | let expected = *info.paramTypes[i]; |
| 12094 | let argExclusive = isExclusiveArgument(expected) or createsCellBorrow(arg); |
| 12095 | if argExclusive { |
| 12096 | try checkPatternLoan(checker, arg); |
| 12097 | } |
| 12098 | if not isUnsafePointerType(expected) { |
| 12099 | for j in 0..argumentsLen { |
| 12100 | let previous = arguments[j] else panic "checkLinearCall: missing active argument"; |
| 12101 | if previous.exclusive or argExclusive { |
| 12102 | if let name = callArgumentConflict(checker.resolver, previous.node, arg) { |
| 12103 | throw emitError(checker.resolver, arg, ErrorKind::BorrowConflict(name)); |
| 12104 | } |
| 12105 | } |
| 12106 | } |
| 12107 | set arguments[argumentsLen] = CallArgument { node: arg, exclusive: argExclusive }; |
| 12108 | set argumentsLen += 1; |
| 12109 | } |
| 12110 | try checkLocalLoans(checker, env, arg, argExclusive); |
| 12111 | let argumentUse = callParameterUse( |
| 12112 | checker.resolver, info, i, contractReceiver, contractReceiverMutable, |
| 12113 | ); |
| 12114 | try checkLinearNode(checker, env, arg, argumentUse); |
| 12115 | if isRefType(expected) and createsExplicitBorrow(arg) { |
| 12116 | try retainCallLoan(checker, arg, argExclusive); |
| 12117 | } |
| 12118 | } |
| 12119 | set checker.localLen = localStart; |
| 12120 | if *info.returnType == Type::Never and info.throwList.len == 0 { |
| 12121 | set env.terminated = true; |
| 12122 | } |
| 12123 | } |
| 12124 | |
| 12125 | /// Return the storage name when two call arguments can address the same place. |
| 12126 | unsafe fn callArgumentConflict 'arena ( |
| 12127 | self: &mut Resolver 'arena, left: *ast::Node, right: *ast::Node |
| 12128 | ) -> ?*[u8] { |
| 12129 | match left.value { |
| 12130 | case ast::NodeValue::CondExpr(cond) => { |
| 12131 | if let name = callArgumentConflict(self, cond.thenExpr, right) { |
| 12132 | return name; |
| 12133 | } |
| 12134 | return callArgumentConflict(self, cond.elseExpr, right); |
| 12135 | } |
| 12136 | case ast::NodeValue::As(cast) => return callArgumentConflict(self, cast.value, right), |
| 12137 | case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, value, right), |
| 12138 | else => {} |
| 12139 | } |
| 12140 | match right.value { |
| 12141 | case ast::NodeValue::CondExpr(cond) => { |
| 12142 | if let name = callArgumentConflict(self, left, cond.thenExpr) { |
| 12143 | return name; |
| 12144 | } |
| 12145 | return callArgumentConflict(self, left, cond.elseExpr); |
| 12146 | } |
| 12147 | case ast::NodeValue::As(cast) => return callArgumentConflict(self, left, cast.value), |
| 12148 | case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, left, value), |
| 12149 | else => {} |
| 12150 | } |
| 12151 | let leftPlace = borrowPlace(self, left); |
| 12152 | let rightPlace = borrowPlace(self, right); |
| 12153 | if placesOverlap(&leftPlace, &rightPlace) { |
| 12154 | let root = rightPlace.root else panic "callArgumentConflict: overlap without root"; |
| 12155 | return root.name; |
| 12156 | } |
| 12157 | return nil; |
| 12158 | } |
| 12159 | |
| 12160 | /// Return whether evaluating an argument creates an explicit address borrow. |
| 12161 | fn createsExplicitBorrow(node: *ast::Node) -> bool { |
| 12162 | match node.value { |
| 12163 | case ast::NodeValue::AddressOf(_) => return true, |
| 12164 | case ast::NodeValue::As(cast) => return createsExplicitBorrow(cast.value), |
| 12165 | case ast::NodeValue::RegionApply { value, .. } => return createsExplicitBorrow(value), |
| 12166 | case ast::NodeValue::CondExpr(cond) => |
| 12167 | return createsExplicitBorrow(cond.thenExpr) or createsExplicitBorrow(cond.elseExpr), |
| 12168 | else => return false, |
| 12169 | } |
| 12170 | } |
| 12171 | |
| 12172 | /// Protect explicit address arguments until their call begins. |
| 12173 | unsafe fn retainCallLoan 'arena 'checking ( |
| 12174 | checker: &mut LinearChecker 'arena 'checking, node: *ast::Node, exclusive: bool |
| 12175 | ) throws (ResolveError) where 'arena: 'checking { |
| 12176 | match node.value { |
| 12177 | case ast::NodeValue::CondExpr(cond) => { |
| 12178 | try retainCallLoan(checker, cond.thenExpr, exclusive); |
| 12179 | try retainCallLoan(checker, cond.elseExpr, exclusive); |
| 12180 | return; |
| 12181 | } |
| 12182 | case ast::NodeValue::As(cast) => { |
| 12183 | try retainCallLoan(checker, cast.value, exclusive); |
| 12184 | return; |
| 12185 | } |
| 12186 | case ast::NodeValue::RegionApply { value, .. } => { |
| 12187 | try retainCallLoan(checker, value, exclusive); |
| 12188 | return; |
| 12189 | } |
| 12190 | else => {} |
| 12191 | } |
| 12192 | let place = borrowPlace(checker.resolver, node); |
| 12193 | if place.root == nil { |
| 12194 | return; |
| 12195 | } |
| 12196 | if checker.localLen >= MAX_LINEAR_BINDINGS { |
| 12197 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
| 12198 | } |
| 12199 | let mut permission: ?*unsafe types::Region = nil; |
| 12200 | let mut storage: ?*unsafe types::Region = nil; |
| 12201 | if let cellTy = try explicitCellPayload(checker.resolver, node) { |
| 12202 | let case Type::Cell { |
| 12203 | class, permission: cellPermission, .. |
| 12204 | } = cellTy else panic "retainCallLoan: invalid cell payload"; |
| 12205 | set permission = cellPermission; |
| 12206 | if let case types::PointerClass::Region(region) = class { |
| 12207 | set storage = region; |
| 12208 | } |
| 12209 | } |
| 12210 | set checker.locals[checker.localLen] = LocalLoan { |
| 12211 | binding: nil, place, exclusive, permission, storage, |
| 12212 | }; |
| 12213 | set checker.localLen += 1; |
| 12214 | } |
| 12215 | |
| 12216 | /// Check a pattern conditional. Linear scrutinees require an exhaustive match. |
| 12217 | unsafe fn checkLinearIfLet 'arena 'checking ( |
| 12218 | checker: &mut LinearChecker 'arena 'checking, |
| 12219 | env: &mut LinearEnv, |
| 12220 | node: *ast::Node, |
| 12221 | conditional: ast::IfLet, |
| 12222 | ) throws (ResolveError) where 'arena: 'checking { |
| 12223 | if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee); |
| 12224 | isLinear(subjectTy) |
| 12225 | { |
| 12226 | throw emitError( |
| 12227 | checker.resolver, |
| 12228 | conditional.pattern.scrutinee, |
| 12229 | ErrorKind::LinearPartialMove, |
| 12230 | ); |
| 12231 | } |
| 12232 | try checkLinearNode( |
| 12233 | checker, |
| 12234 | env, |
| 12235 | conditional.pattern.scrutinee, |
| 12236 | patternSubjectUse(checker.resolver, conditional.pattern.scrutinee), |
| 12237 | ); |
| 12238 | let base = *env; |
| 12239 | let mut thenEnv = base; |
| 12240 | let bindingsStart = thenEnv.len; |
| 12241 | let loanStart = checker.loanLen; |
| 12242 | if try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern) { |
| 12243 | try addPatternLoan(checker, conditional.pattern.scrutinee); |
| 12244 | } |
| 12245 | expireAllocationLoans(checker, &mut thenEnv, conditional.pattern.pattern, conditional.pattern.scrutinee); |
| 12246 | if let guard = conditional.pattern.guard { |
| 12247 | try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume); |
| 12248 | } |
| 12249 | try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard); |
| 12250 | try finishLinearScope(checker, &mut thenEnv, bindingsStart); |
| 12251 | set checker.loanLen = loanStart; |
| 12252 | let mut elseEnv = base; |
| 12253 | if let branch = conditional.elseBranch { |
| 12254 | try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard); |
| 12255 | } |
| 12256 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
| 12257 | } |
| 12258 | |
| 12259 | /// Check a repeated region-loan flow until its loop-entry mask is stable. |
| 12260 | /// Each additional pass must add a bit from the bounded regional loan table. |
| 12261 | unsafe fn checkLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
| 12262 | throws (ResolveError) where 'arena: 'checking |
| 12263 | { |
| 12264 | if let case ast::NodeValue::For(forStmt) = node.value { |
| 12265 | try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume); |
| 12266 | } |
| 12267 | let base = *env; |
| 12268 | let depth = checker.loopDepth; |
| 12269 | let mut entryLoans = base.regionalLoans; |
| 12270 | loop { |
| 12271 | let mut pass = base; |
| 12272 | set pass.regionalLoans = entryLoans; |
| 12273 | try checkLinearLoopPass(checker, &mut pass, node); |
| 12274 | let next = entryLoans | checker.loopBackLoans[depth]; |
| 12275 | if next == entryLoans { |
| 12276 | set *env = pass; |
| 12277 | return; |
| 12278 | } |
| 12279 | set entryLoans = next; |
| 12280 | } |
| 12281 | } |
| 12282 | |
| 12283 | /// Check one pass through a loop with the current loop-entry loan state. |
| 12284 | unsafe fn checkLinearLoopPass 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
| 12285 | throws (ResolveError) where 'arena: 'checking |
| 12286 | { |
| 12287 | match node.value { |
| 12288 | case ast::NodeValue::While(whileStmt) => { |
| 12289 | enterLinearLoop(checker, env); |
| 12290 | try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume); |
| 12291 | let conditionExit = *env; |
| 12292 | setLinearLoopNaturalExit(checker, &conditionExit); |
| 12293 | let mut bodyEnv = conditionExit; |
| 12294 | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
| 12295 | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
| 12296 | exitLinearLoop(checker); |
| 12297 | set *env = conditionExit; |
| 12298 | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
| 12299 | if let elseBranch = whileStmt.elseBranch { |
| 12300 | let mut elseEnv = conditionExit; |
| 12301 | try checkLinearNode( |
| 12302 | checker, |
| 12303 | &mut elseEnv, |
| 12304 | elseBranch, |
| 12305 | LinearUse::Discard, |
| 12306 | ); |
| 12307 | let exits = *env; |
| 12308 | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
| 12309 | } |
| 12310 | } |
| 12311 | case ast::NodeValue::WhileLet(whileStmt) => { |
| 12312 | if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee); |
| 12313 | isLinear(subjectTy) |
| 12314 | { |
| 12315 | throw emitError( |
| 12316 | checker.resolver, |
| 12317 | whileStmt.pattern.scrutinee, |
| 12318 | ErrorKind::LinearPartialMove, |
| 12319 | ); |
| 12320 | } |
| 12321 | let base = *env; |
| 12322 | enterLinearLoop(checker, env); |
| 12323 | let mut bodyEnv = base; |
| 12324 | try checkLinearNode( |
| 12325 | checker, |
| 12326 | &mut bodyEnv, |
| 12327 | whileStmt.pattern.scrutinee, |
| 12328 | patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee), |
| 12329 | ); |
| 12330 | let mut conditionExit = bodyEnv; |
| 12331 | let start = bodyEnv.len; |
| 12332 | let loanStart = checker.loanLen; |
| 12333 | if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) { |
| 12334 | try addPatternLoan(checker, whileStmt.pattern.scrutinee); |
| 12335 | } |
| 12336 | expireAllocationLoans(checker, &mut bodyEnv, whileStmt.pattern.pattern, whileStmt.pattern.scrutinee); |
| 12337 | if let guard = whileStmt.pattern.guard { |
| 12338 | try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume); |
| 12339 | let mut guardExit = bodyEnv; |
| 12340 | try finishLinearScope(checker, &mut guardExit, start); |
| 12341 | let previous = conditionExit; |
| 12342 | try joinLinearBranches( |
| 12343 | checker, |
| 12344 | &mut conditionExit, |
| 12345 | &previous, |
| 12346 | &guardExit, |
| 12347 | guard, |
| 12348 | ); |
| 12349 | } |
| 12350 | setLinearLoopNaturalExit(checker, &conditionExit); |
| 12351 | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
| 12352 | try finishLinearScope(checker, &mut bodyEnv, start); |
| 12353 | set checker.loanLen = loanStart; |
| 12354 | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
| 12355 | exitLinearLoop(checker); |
| 12356 | set *env = conditionExit; |
| 12357 | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
| 12358 | if let elseBranch = whileStmt.elseBranch { |
| 12359 | let mut elseEnv = conditionExit; |
| 12360 | try checkLinearNode( |
| 12361 | checker, |
| 12362 | &mut elseEnv, |
| 12363 | elseBranch, |
| 12364 | LinearUse::Discard, |
| 12365 | ); |
| 12366 | let exits = *env; |
| 12367 | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
| 12368 | } |
| 12369 | } |
| 12370 | case ast::NodeValue::For(forStmt) => { |
| 12371 | if let iterableTy = typeFor(checker.resolver, forStmt.iterable) { |
| 12372 | if isLinear(iterableTy) { |
| 12373 | throw emitError( |
| 12374 | checker.resolver, |
| 12375 | forStmt.iterable, |
| 12376 | ErrorKind::LinearPartialMove, |
| 12377 | ); |
| 12378 | } |
| 12379 | } |
| 12380 | let base = *env; |
| 12381 | enterLinearLoop(checker, env); |
| 12382 | setLinearLoopNaturalExit(checker, &base); |
| 12383 | let mut bodyEnv = base; |
| 12384 | let start = bodyEnv.len; |
| 12385 | try addLinearBinding(checker, &mut bodyEnv, forStmt.binding); |
| 12386 | if let index = forStmt.index { |
| 12387 | try addLinearBinding(checker, &mut bodyEnv, index); |
| 12388 | } |
| 12389 | try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard); |
| 12390 | try finishLinearScope(checker, &mut bodyEnv, start); |
| 12391 | try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body); |
| 12392 | exitLinearLoop(checker); |
| 12393 | set *env = base; |
| 12394 | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
| 12395 | if let elseBranch = forStmt.elseBranch { |
| 12396 | let mut elseEnv = base; |
| 12397 | try checkLinearNode( |
| 12398 | checker, |
| 12399 | &mut elseEnv, |
| 12400 | elseBranch, |
| 12401 | LinearUse::Discard, |
| 12402 | ); |
| 12403 | let exits = *env; |
| 12404 | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
| 12405 | } |
| 12406 | } |
| 12407 | case ast::NodeValue::Loop { body } => { |
| 12408 | let base = *env; |
| 12409 | enterLinearLoop(checker, env); |
| 12410 | let mut bodyEnv = base; |
| 12411 | try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard); |
| 12412 | try checkLinearLoopBackEdge(checker, &bodyEnv, body); |
| 12413 | let depth = checker.loopDepth - 1; |
| 12414 | let breakSeen = checker.loopBreakSeen[depth]; |
| 12415 | let exitAvailable = checker.loopExitAvailable[depth]; |
| 12416 | exitLinearLoop(checker); |
| 12417 | set *env = base; |
| 12418 | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
| 12419 | if breakSeen { |
| 12420 | set env.available = exitAvailable; |
| 12421 | } else { |
| 12422 | set env.terminated = true; |
| 12423 | } |
| 12424 | } |
| 12425 | else => panic "checkLinearLoopPass: expected loop", |
| 12426 | } |
| 12427 | } |
| 12428 | |
| 12429 | /// Transfer a cell's owning handle and check each address operand once. |
| 12430 | unsafe fn checkCellAddressOwner 'arena 'checking ( |
| 12431 | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, |
| 12432 | node: *ast::Node, owner: *ast::Node |
| 12433 | ) throws (ResolveError) where 'arena: 'checking { |
| 12434 | if node == owner { |
| 12435 | try checkLinearNode(checker, env, node, LinearUse::Consume); |
| 12436 | return; |
| 12437 | } |
| 12438 | match node.value { |
| 12439 | case ast::NodeValue::Deref(target) => try checkCellAddressOwner(checker, env, target, owner), |
| 12440 | case ast::NodeValue::FieldAccess(access) => try checkCellAddressOwner(checker, env, access.parent, owner), |
| 12441 | case ast::NodeValue::Subscript { container, index } => { |
| 12442 | try checkCellAddressOwner(checker, env, container, owner); |
| 12443 | try checkLinearNode(checker, env, index, LinearUse::Consume); |
| 12444 | } |
| 12445 | else => panic "cell address owner must belong to its place", |
| 12446 | } |
| 12447 | } |
| 12448 | |
| 12449 | /// Fresh allocation storage has no loans from earlier loop iterations. |
| 12450 | unsafe fn expireAllocationLoans 'arena 'checking ( |
| 12451 | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, |
| 12452 | node: *ast::Node, value: *ast::Node |
| 12453 | ) where 'arena: 'checking { |
| 12454 | let case ast::NodeValue::Try(allocation) = value.value else return; |
| 12455 | if allocation.catches.len <> 0 { |
| 12456 | return; |
| 12457 | } |
| 12458 | let case NodeExtra::SessionAllocation(_) = checker.resolver.nodeData.entries[allocation.expr.id].extra |
| 12459 | else return; |
| 12460 | let symbol = checker.resolver.nodeData.entries[node.id].binding else return; |
| 12461 | for i in 0..checker.regionalLen { |
| 12462 | if regionalLoan(checker, i).place.root == symbol.symbol { |
| 12463 | set env.regionalLoans &= ~((1 as u64) << (i as u64)); |
| 12464 | } |
| 12465 | } |
| 12466 | } |
| 12467 | |
| 12468 | /// Check one expression or statement under an ownership-use context. |
| 12469 | unsafe fn checkLinearNode 'arena 'checking ( |
| 12470 | checker: &mut LinearChecker 'arena 'checking, |
| 12471 | env: &mut LinearEnv, |
| 12472 | node: *ast::Node, |
| 12473 | usage: LinearUse, |
| 12474 | ) throws (ResolveError) where 'arena: 'checking { |
| 12475 | if env.terminated { |
| 12476 | return; |
| 12477 | } |
| 12478 | if usage <> LinearUse::Locate { |
| 12479 | match node.value { |
| 12480 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_), |
| 12481 | ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. }, |
| 12482 | ast::NodeValue::Deref(_) => { |
| 12483 | let mut exclusive = usage == LinearUse::Place |
| 12484 | and not isCellDeref(checker.resolver, node); |
| 12485 | if usage == LinearUse::Consume { |
| 12486 | if let ty = typeFor(checker.resolver, node) { |
| 12487 | set exclusive = isExclusiveArgument(ty); |
| 12488 | } |
| 12489 | } |
| 12490 | if let cellTy = try inferCellPayload(checker.resolver, node) { |
| 12491 | let case Type::Cell { permission: controlled, .. } = cellTy |
| 12492 | else panic "checkLinearNode: invalid cell payload"; |
| 12493 | if let permission = controlled { |
| 12494 | let permissionExclusive = usage == LinearUse::Place; |
| 12495 | for i in 0..checker.localLen { |
| 12496 | let loan = checker.locals[i]; |
| 12497 | if let loanPermission = loan.permission; |
| 12498 | loanPermission.id == permission.id |
| 12499 | and (permissionExclusive or loan.exclusive) |
| 12500 | { |
| 12501 | throw emitError( |
| 12502 | checker.resolver, node, |
| 12503 | ErrorKind::BorrowConflict(permission.name), |
| 12504 | ); |
| 12505 | } |
| 12506 | } |
| 12507 | let index = findCellAuthority(checker, env, permission) |
| 12508 | else throw emitError( |
| 12509 | checker.resolver, node, |
| 12510 | ErrorKind::CellPermissionRequired(permission.name), |
| 12511 | ); |
| 12512 | if permissionExclusive and not checker.authorityExclusive[index] { |
| 12513 | throw emitError( |
| 12514 | checker.resolver, node, |
| 12515 | ErrorKind::CellPermissionRequired(permission.name), |
| 12516 | ); |
| 12517 | } |
| 12518 | } |
| 12519 | } |
| 12520 | try checkLocalLoans(checker, env, node, exclusive); |
| 12521 | } |
| 12522 | else => {} |
| 12523 | } |
| 12524 | } |
| 12525 | match node.value { |
| 12526 | case ast::NodeValue::Ident(_) => { |
| 12527 | if usage <> LinearUse::Place { |
| 12528 | try checkLinearIdent(checker, env, node); |
| 12529 | } |
| 12530 | if usage == LinearUse::Consume { |
| 12531 | if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) { |
| 12532 | try checkPatternLoan(checker, node); |
| 12533 | } |
| 12534 | try consumeLinearIdent(checker, env, node); |
| 12535 | } |
| 12536 | } |
| 12537 | case ast::NodeValue::ExprStmt(expr) => { |
| 12538 | if let exprTy = typeFor(checker.resolver, expr) { |
| 12539 | if isLinear(exprTy) { |
| 12540 | throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard); |
| 12541 | } |
| 12542 | } |
| 12543 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
| 12544 | } |
| 12545 | case ast::NodeValue::RegionBlock { bindings, body, .. } => { |
| 12546 | let previousRegions = checker.regions; |
| 12547 | let case NodeExtra::Regions(scope) = checker.resolver.nodeData.entries[node.id].extra else { |
| 12548 | if checker.resolver.errors.len > 0 { |
| 12549 | return; |
| 12550 | } |
| 12551 | panic "checkLinearNode: missing region scope"; |
| 12552 | }; |
| 12553 | let start = env.len; |
| 12554 | let localStart = checker.localLen; |
| 12555 | let authorityStart = checker.authorityLen; |
| 12556 | let mut reborrowSources: [?*ast::Node; MAX_LINEAR_BINDINGS] = |
| 12557 | [nil; MAX_LINEAR_BINDINGS]; |
| 12558 | let mut reborrowPermissions: [?*unsafe types::Region; MAX_LINEAR_BINDINGS] = |
| 12559 | [nil; MAX_LINEAR_BINDINGS]; |
| 12560 | let mut reborrowExclusive: [bool; MAX_LINEAR_BINDINGS] = |
| 12561 | [false; MAX_LINEAR_BINDINGS]; |
| 12562 | let mut reborrowLen: u32 = 0; |
| 12563 | for bindingNode in bindings { |
| 12564 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 12565 | else panic "checkLinearNode: invalid borrow binding"; |
| 12566 | let case ast::NodeValue::AddressOf(address) = binding.value.value |
| 12567 | else continue; |
| 12568 | if address.kind == ast::AddressKind::Cell { |
| 12569 | continue; |
| 12570 | } |
| 12571 | let case ast::NodeValue::Deref(source) = address.target.value |
| 12572 | else continue; |
| 12573 | let sourceTy = typeFor(checker.resolver, source) else continue; |
| 12574 | let case Type::Pointer { |
| 12575 | class: types::PointerClass::Region(permission), |
| 12576 | mutable: true, |
| 12577 | .. |
| 12578 | } = sourceTy else continue; |
| 12579 | let authorityIndex = findCellAuthority(checker, env, permission) |
| 12580 | else continue; |
| 12581 | if not checker.authorityExclusive[authorityIndex] { |
| 12582 | continue; |
| 12583 | } |
| 12584 | assert reborrowLen < MAX_LINEAR_BINDINGS, |
| 12585 | "checkLinearNode: permission reborrow overflow"; |
| 12586 | set reborrowSources[reborrowLen] = binding.value; |
| 12587 | set reborrowPermissions[reborrowLen] = permission; |
| 12588 | set reborrowExclusive[reborrowLen] = |
| 12589 | address.kind == ast::AddressKind::Mutable; |
| 12590 | set reborrowLen += 1; |
| 12591 | } |
| 12592 | set checker.regions = scope; |
| 12593 | for bindingNode in bindings { |
| 12594 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
| 12595 | else panic "checkLinearNode: invalid borrow binding"; |
| 12596 | let case ast::NodeValue::AddressOf(address) = binding.value.value |
| 12597 | else panic "checkLinearNode: invalid borrow source"; |
| 12598 | if let cellTy = try inferCellPayload(checker.resolver, address.target) { |
| 12599 | let case Type::Cell { permission: controlled, .. } = cellTy |
| 12600 | else panic "checkLinearNode: invalid cell payload"; |
| 12601 | if let permission = controlled { |
| 12602 | let exclusive = address.kind == ast::AddressKind::Mutable; |
| 12603 | let mut matched = false; |
| 12604 | for i in 0..reborrowLen { |
| 12605 | let candidate = reborrowPermissions[i] |
| 12606 | else panic "checkLinearNode: missing reborrow permission"; |
| 12607 | if candidate.id == permission.id |
| 12608 | and reborrowExclusive[i] == exclusive |
| 12609 | { |
| 12610 | set matched = true; |
| 12611 | break; |
| 12612 | } |
| 12613 | } |
| 12614 | if not matched { |
| 12615 | throw emitError( |
| 12616 | checker.resolver, binding.value, |
| 12617 | ErrorKind::CellPermissionRequired(permission.name), |
| 12618 | ); |
| 12619 | } |
| 12620 | set checker.payloadAddress = binding.value; |
| 12621 | } |
| 12622 | } |
| 12623 | for i in 0..reborrowLen { |
| 12624 | let source = reborrowSources[i] |
| 12625 | else panic "checkLinearNode: missing authority reborrow"; |
| 12626 | if source == binding.value { |
| 12627 | set checker.witnessPermission = reborrowPermissions[i]; |
| 12628 | break; |
| 12629 | } |
| 12630 | } |
| 12631 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
| 12632 | if env.terminated { |
| 12633 | break; |
| 12634 | } |
| 12635 | try addLinearBinding(checker, env, bindingNode); |
| 12636 | let lexical = ast::borrowBinding(binding); |
| 12637 | try addLocalLoan(checker, env, bindingNode, lexical); |
| 12638 | let symbol = symbolFor(checker.resolver, bindingNode) |
| 12639 | else panic "checkLinearNode: missing region binding"; |
| 12640 | let mut permission = checker.witnessPermission; |
| 12641 | let mut exclusive = address.kind == ast::AddressKind::Mutable; |
| 12642 | if permission == nil { |
| 12643 | if let case SymbolData::Value { |
| 12644 | type: Type::Pointer { |
| 12645 | class: types::PointerClass::Region(region), |
| 12646 | mutable: true, |
| 12647 | .. |
| 12648 | }, |
| 12649 | .. |
| 12650 | } = symbol.data { |
| 12651 | set permission = region; |
| 12652 | set exclusive = true; |
| 12653 | } |
| 12654 | } |
| 12655 | if let controlled = permission { |
| 12656 | if checker.authorityLen >= MAX_LINEAR_BINDINGS { |
| 12657 | throw emitError(checker.resolver, bindingNode, ErrorKind::Internal); |
| 12658 | } |
| 12659 | let index = checker.authorityLen; |
| 12660 | set checker.authorityPermissions[index] = controlled; |
| 12661 | set checker.authorityBindings[index] = symbol; |
| 12662 | set checker.authorityExclusive[index] = exclusive; |
| 12663 | set checker.authorityLen += 1; |
| 12664 | } |
| 12665 | set checker.payloadAddress = nil; |
| 12666 | set checker.witnessPermission = nil; |
| 12667 | } |
| 12668 | try checkLinearBlock(checker, env, body); |
| 12669 | try finishLinearScope(checker, env, start); |
| 12670 | set checker.localLen = localStart; |
| 12671 | set checker.authorityLen = authorityStart; |
| 12672 | set checker.regions = previousRegions; |
| 12673 | compactRegionalLoans(checker, env, previousRegions); |
| 12674 | } |
| 12675 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
| 12676 | case ast::NodeValue::Let(binding) => { |
| 12677 | let mut isUndefined = false; |
| 12678 | if let case ast::NodeValue::Undef = binding.value.value { |
| 12679 | set isUndefined = true; |
| 12680 | } |
| 12681 | if isUndefined { |
| 12682 | if let bindingTy = typeFor(checker.resolver, binding.ident); |
| 12683 | isLinear(bindingTy) |
| 12684 | { |
| 12685 | throw emitError( |
| 12686 | checker.resolver, |
| 12687 | binding.value, |
| 12688 | ErrorKind::LinearUndefined, |
| 12689 | ); |
| 12690 | } |
| 12691 | } |
| 12692 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
| 12693 | if env.terminated { |
| 12694 | return; |
| 12695 | } |
| 12696 | try addLinearBinding(checker, env, node); |
| 12697 | try addLocalLoan(checker, env, node, binding); |
| 12698 | if let symbol = symbolFor(checker.resolver, node) { |
| 12699 | if let case SymbolData::Value { |
| 12700 | type: Type::Pointer { |
| 12701 | class: types::PointerClass::Region(permission), |
| 12702 | mutable: true, |
| 12703 | .. |
| 12704 | }, |
| 12705 | .. |
| 12706 | } = symbol.data { |
| 12707 | if checker.authorityLen >= MAX_LINEAR_BINDINGS { |
| 12708 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
| 12709 | } |
| 12710 | let index = checker.authorityLen; |
| 12711 | set checker.authorityPermissions[index] = permission; |
| 12712 | set checker.authorityBindings[index] = symbol; |
| 12713 | set checker.authorityExclusive[index] = true; |
| 12714 | set checker.authorityLen += 1; |
| 12715 | } |
| 12716 | } |
| 12717 | expireAllocationLoans(checker, env, node, binding.value); |
| 12718 | if isUndefined { |
| 12719 | markLinearBindingUnavailable(checker.resolver, env, node); |
| 12720 | } |
| 12721 | } |
| 12722 | case ast::NodeValue::Assign(assign) => { |
| 12723 | if not isCellDeref(checker.resolver, assign.left) { |
| 12724 | try checkPatternLoan(checker, assign.left); |
| 12725 | } |
| 12726 | let mut target: ?u32 = nil; |
| 12727 | let mut targetLinear = false; |
| 12728 | if let leftTy = typeFor(checker.resolver, assign.left) { |
| 12729 | if isMoveOnly(leftTy) { |
| 12730 | set targetLinear = isLinear(leftTy); |
| 12731 | if let case ast::NodeValue::Ident(_) = assign.left.value { |
| 12732 | if let sym = symbolFor(checker.resolver, assign.left) { |
| 12733 | set target = findLinearBinding(env, sym.id); |
| 12734 | } |
| 12735 | } |
| 12736 | if targetLinear and target == nil { |
| 12737 | throw emitError( |
| 12738 | checker.resolver, |
| 12739 | assign.left, |
| 12740 | ErrorKind::LinearOverwrite, |
| 12741 | ); |
| 12742 | } |
| 12743 | } |
| 12744 | } |
| 12745 | try checkLinearNode(checker, env, assign.left, LinearUse::Place); |
| 12746 | try checkLinearNode(checker, env, assign.right, LinearUse::Consume); |
| 12747 | if let index = target { |
| 12748 | if targetLinear and linearBindingAvailable(env, index) { |
| 12749 | throw emitError( |
| 12750 | checker.resolver, |
| 12751 | assign.left, |
| 12752 | ErrorKind::LinearOverwrite, |
| 12753 | ); |
| 12754 | } |
| 12755 | set env.available |= (1 as u64) << (index as u64); |
| 12756 | } |
| 12757 | } |
| 12758 | case ast::NodeValue::RegionApply { value, .. } => |
| 12759 | try checkLinearNode(checker, env, value, usage), |
| 12760 | case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call), |
| 12761 | case ast::NodeValue::AddressOf(addr) => { |
| 12762 | if addr.kind == ast::AddressKind::Cell { |
| 12763 | if let resultTy = typeFor(checker.resolver, node) { |
| 12764 | if let case Type::Cell { permission: controlled, .. } = resultTy { |
| 12765 | if let permission = controlled { |
| 12766 | let index = findCellAuthority(checker, env, permission) |
| 12767 | else throw emitError( |
| 12768 | checker.resolver, node, |
| 12769 | ErrorKind::CellPermissionRequired(permission.name), |
| 12770 | ); |
| 12771 | if not checker.authorityExclusive[index] { |
| 12772 | throw emitError( |
| 12773 | checker.resolver, node, |
| 12774 | ErrorKind::CellPermissionRequired(permission.name), |
| 12775 | ); |
| 12776 | } |
| 12777 | } |
| 12778 | } |
| 12779 | } |
| 12780 | } |
| 12781 | if let cellTy = try inferCellPayload(checker.resolver, addr.target) { |
| 12782 | let case Type::Cell { permission: controlled, .. } = cellTy |
| 12783 | else panic "checkLinearNode: invalid cell payload address"; |
| 12784 | if let permission = controlled { |
| 12785 | let mut headerValidated = false; |
| 12786 | if let allowed = checker.payloadAddress; allowed == node { |
| 12787 | set headerValidated = true; |
| 12788 | } |
| 12789 | if not headerValidated { |
| 12790 | let index = findCellAuthority(checker, env, permission) |
| 12791 | else throw emitError( |
| 12792 | checker.resolver, node, |
| 12793 | ErrorKind::CellPermissionRequired(permission.name), |
| 12794 | ); |
| 12795 | if addr.kind == ast::AddressKind::Mutable |
| 12796 | and not checker.authorityExclusive[index] |
| 12797 | { |
| 12798 | throw emitError( |
| 12799 | checker.resolver, node, |
| 12800 | ErrorKind::CellPermissionRequired(permission.name), |
| 12801 | ); |
| 12802 | } |
| 12803 | } |
| 12804 | let permissionExclusive = addr.kind == ast::AddressKind::Mutable; |
| 12805 | for i in 0..checker.localLen { |
| 12806 | let loan = checker.locals[i]; |
| 12807 | if let loanPermission = loan.permission; |
| 12808 | loanPermission.id == permission.id |
| 12809 | and (permissionExclusive or loan.exclusive) |
| 12810 | { |
| 12811 | throw emitError( |
| 12812 | checker.resolver, node, |
| 12813 | ErrorKind::BorrowConflict(permission.name), |
| 12814 | ); |
| 12815 | } |
| 12816 | } |
| 12817 | } |
| 12818 | } |
| 12819 | try checkLocalLoans(checker, env, addr.target, ast::isExclusiveAddress(addr)); |
| 12820 | if ast::isExclusiveAddress(addr) { |
| 12821 | try checkPatternLoan(checker, addr.target); |
| 12822 | } |
| 12823 | let mut owner: ?*ast::Node = nil; |
| 12824 | if addr.kind == ast::AddressKind::Cell { |
| 12825 | if let ty = typeFor(checker.resolver, node) { |
| 12826 | if let case Type::Cell { class: types::PointerClass::Owned, .. } = ty { |
| 12827 | set owner = addressOwner(checker.resolver, addr.target); |
| 12828 | } |
| 12829 | } |
| 12830 | } |
| 12831 | if let source = owner { |
| 12832 | try checkCellAddressOwner(checker, env, addr.target, source); |
| 12833 | } else { |
| 12834 | try checkLinearNode(checker, env, addr.target, LinearUse::Locate); |
| 12835 | } |
| 12836 | try addRegionalLoan(checker, env, node, addr); |
| 12837 | } |
| 12838 | case ast::NodeValue::Deref(target) => { |
| 12839 | if let resultTy = typeFor(checker.resolver, node) { |
| 12840 | let cellObservation = usage == LinearUse::Observe |
| 12841 | and (try inferCellPayload(checker.resolver, node)) <> nil; |
| 12842 | if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) { |
| 12843 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
| 12844 | } |
| 12845 | } |
| 12846 | try checkLinearNode(checker, env, target, LinearUse::Locate); |
| 12847 | } |
| 12848 | case ast::NodeValue::FieldAccess(access) => { |
| 12849 | if let resultTy = typeFor(checker.resolver, node) { |
| 12850 | let cellObservation = usage == LinearUse::Observe |
| 12851 | and (try inferCellPayload(checker.resolver, node)) <> nil; |
| 12852 | if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) { |
| 12853 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
| 12854 | } |
| 12855 | } |
| 12856 | try checkLinearNode(checker, env, access.parent, LinearUse::Locate); |
| 12857 | } |
| 12858 | case ast::NodeValue::ScopeAccess(_) => {} |
| 12859 | case ast::NodeValue::Subscript { container, index } => { |
| 12860 | if let resultTy = typeFor(checker.resolver, node) { |
| 12861 | let cellObservation = usage == LinearUse::Observe |
| 12862 | and (try inferCellPayload(checker.resolver, node)) <> nil; |
| 12863 | if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) { |
| 12864 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
| 12865 | } |
| 12866 | } |
| 12867 | try checkLinearNode(checker, env, container, LinearUse::Locate); |
| 12868 | try checkLinearNode(checker, env, index, LinearUse::Consume); |
| 12869 | } |
| 12870 | case ast::NodeValue::RecordLit(lit) => { |
| 12871 | for fieldNode in lit.fields { |
| 12872 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value |
| 12873 | else panic "checkLinearNode: expected field"; |
| 12874 | try checkLinearNode(checker, env, field.value, LinearUse::Consume); |
| 12875 | } |
| 12876 | } |
| 12877 | case ast::NodeValue::ArrayLit(items) => { |
| 12878 | for item in items { |
| 12879 | try checkLinearNode(checker, env, item, LinearUse::Consume); |
| 12880 | } |
| 12881 | } |
| 12882 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
| 12883 | if let itemTy = typeFor(checker.resolver, repeat.item) { |
| 12884 | if not isCopy(itemTy) { |
| 12885 | throw emitError( |
| 12886 | checker.resolver, |
| 12887 | repeat.item, |
| 12888 | ErrorKind::LinearDiscard, |
| 12889 | ); |
| 12890 | } |
| 12891 | } |
| 12892 | try checkLinearNode(checker, env, repeat.item, LinearUse::Consume); |
| 12893 | try checkLinearNode(checker, env, repeat.count, LinearUse::Consume); |
| 12894 | } |
| 12895 | case ast::NodeValue::BinOp(op) => { |
| 12896 | if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or { |
| 12897 | try checkLinearNode(checker, env, op.left, LinearUse::Consume); |
| 12898 | let skipped = *env; |
| 12899 | let mut evaluated = skipped; |
| 12900 | try checkLinearNode(checker, &mut evaluated, op.right, LinearUse::Consume); |
| 12901 | try joinLinearBranches(checker, env, &skipped, &evaluated, node); |
| 12902 | return; |
| 12903 | } |
| 12904 | let mut operandUse = LinearUse::Consume; |
| 12905 | match op.op { |
| 12906 | case ast::BinaryOp::Eq, ast::BinaryOp::Ne, |
| 12907 | ast::BinaryOp::Lt, ast::BinaryOp::Gt, |
| 12908 | ast::BinaryOp::Lte, ast::BinaryOp::Gte => |
| 12909 | set operandUse = LinearUse::Observe, |
| 12910 | else => {} |
| 12911 | } |
| 12912 | try checkLinearNode(checker, env, op.left, operandUse); |
| 12913 | try checkLinearNode(checker, env, op.right, operandUse); |
| 12914 | } |
| 12915 | case ast::NodeValue::UnOp(op) => { |
| 12916 | try checkLinearNode(checker, env, op.value, LinearUse::Consume); |
| 12917 | } |
| 12918 | case ast::NodeValue::As(expr) => { |
| 12919 | let mut castUse = usage; |
| 12920 | if let targetTy = typeFor(checker.resolver, node) { |
| 12921 | if let case Type::Cell { .. } = targetTy { |
| 12922 | set castUse = LinearUse::Consume; |
| 12923 | } |
| 12924 | } |
| 12925 | if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) { |
| 12926 | set castUse = LinearUse::Observe; |
| 12927 | } |
| 12928 | try checkLinearNode(checker, env, expr.value, castUse); |
| 12929 | } |
| 12930 | case ast::NodeValue::Range(range) => { |
| 12931 | if let start = range.start { |
| 12932 | try checkLinearNode(checker, env, start, LinearUse::Consume); |
| 12933 | } |
| 12934 | if let end = range.end { |
| 12935 | try checkLinearNode(checker, env, end, LinearUse::Consume); |
| 12936 | } |
| 12937 | } |
| 12938 | case ast::NodeValue::BuiltinCall { args, .. } => { |
| 12939 | for arg in args { |
| 12940 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
| 12941 | } |
| 12942 | } |
| 12943 | case ast::NodeValue::If(conditional) => { |
| 12944 | try checkLinearIf(checker, env, node, conditional); |
| 12945 | } |
| 12946 | case ast::NodeValue::CondExpr(conditional) => { |
| 12947 | try checkLinearCondExpr(checker, env, node, conditional, usage); |
| 12948 | } |
| 12949 | case ast::NodeValue::IfLet(conditional) => { |
| 12950 | try checkLinearIfLet(checker, env, node, conditional); |
| 12951 | } |
| 12952 | case ast::NodeValue::LetElse(binding) => { |
| 12953 | if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee); |
| 12954 | isLinear(subjectTy) |
| 12955 | { |
| 12956 | throw emitError( |
| 12957 | checker.resolver, |
| 12958 | binding.pattern.scrutinee, |
| 12959 | ErrorKind::LinearPartialMove, |
| 12960 | ); |
| 12961 | } |
| 12962 | try checkLinearNode( |
| 12963 | checker, |
| 12964 | env, |
| 12965 | binding.pattern.scrutinee, |
| 12966 | patternSubjectUse(checker.resolver, binding.pattern.scrutinee), |
| 12967 | ); |
| 12968 | let base = *env; |
| 12969 | let mut guardedEnv = base; |
| 12970 | try addLinearPatternBindings(checker, &mut guardedEnv, binding.pattern.pattern); |
| 12971 | expireAllocationLoans(checker, &mut guardedEnv, binding.pattern.pattern, binding.pattern.scrutinee); |
| 12972 | if let guard = binding.pattern.guard { |
| 12973 | try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume); |
| 12974 | } |
| 12975 | let mut successEnv = guardedEnv; |
| 12976 | let mut fallbackEnv = base; |
| 12977 | try checkLinearNode( |
| 12978 | checker, |
| 12979 | &mut fallbackEnv, |
| 12980 | binding.elseBranch, |
| 12981 | LinearUse::Consume, |
| 12982 | ); |
| 12983 | if binding.pattern.guard <> nil { |
| 12984 | let mut guardFallbackEnv = guardedEnv; |
| 12985 | try checkLinearNode( |
| 12986 | checker, |
| 12987 | &mut guardFallbackEnv, |
| 12988 | binding.elseBranch, |
| 12989 | LinearUse::Consume, |
| 12990 | ); |
| 12991 | try finishLinearScope(checker, &mut guardFallbackEnv, base.len); |
| 12992 | let previous = fallbackEnv; |
| 12993 | try joinLinearBranches( |
| 12994 | checker, |
| 12995 | &mut fallbackEnv, |
| 12996 | &previous, |
| 12997 | &guardFallbackEnv, |
| 12998 | binding.elseBranch, |
| 12999 | ); |
| 13000 | } |
| 13001 | if let case ast::PatternKind::Binding = binding.pattern.kind { |
| 13002 | try addLinearPatternBindings( |
| 13003 | checker, |
| 13004 | &mut fallbackEnv, |
| 13005 | binding.pattern.pattern, |
| 13006 | ); |
| 13007 | } |
| 13008 | try joinLinearBranches(checker, env, &successEnv, &fallbackEnv, node); |
| 13009 | } |
| 13010 | case ast::NodeValue::Match(matchExpr) => { |
| 13011 | try checkLinearMatch(checker, env, node, matchExpr); |
| 13012 | } |
| 13013 | case ast::NodeValue::Try(tryExpr) => { |
| 13014 | try checkLinearNode(checker, env, tryExpr.expr, usage); |
| 13015 | let success = *env; |
| 13016 | if let resultTy = typeFor(checker.resolver, tryExpr.expr); resultTy == Type::Never { |
| 13017 | if not tryExpr.returnsOptional and (tryExpr.catches.len > 0 or tryExpr.shouldPanic) { |
| 13018 | set env.terminated = true; |
| 13019 | } |
| 13020 | } |
| 13021 | for catchNode in tryExpr.catches { |
| 13022 | let case ast::NodeValue::CatchClause(catchClause) = catchNode.value |
| 13023 | else panic "checkLinearNode: expected catch"; |
| 13024 | let mut branch = success; |
| 13025 | let start = branch.len; |
| 13026 | if let binding = catchClause.binding { |
| 13027 | try addLinearBinding(checker, &mut branch, binding); |
| 13028 | } |
| 13029 | try checkLinearNode(checker, &mut branch, catchClause.body, usage); |
| 13030 | try finishLinearScope(checker, &mut branch, start); |
| 13031 | let previous = *env; |
| 13032 | try joinLinearBranches(checker, env, &previous, &branch, node); |
| 13033 | } |
| 13034 | } |
| 13035 | case ast::NodeValue::While(_), ast::NodeValue::WhileLet(_), |
| 13036 | ast::NodeValue::For(_), ast::NodeValue::Loop { .. } => |
| 13037 | try checkLinearLoop(checker, env, node), |
| 13038 | case ast::NodeValue::Break => { |
| 13039 | assert checker.loopDepth > 0, "linear loop control outside loop"; |
| 13040 | let start = checker.loopMarks[checker.loopDepth - 1]; |
| 13041 | try finishLinearScope(checker, env, start); |
| 13042 | try checkLinearLoopBreak(checker, env, node); |
| 13043 | set env.terminated = true; |
| 13044 | } |
| 13045 | case ast::NodeValue::Continue => { |
| 13046 | assert checker.loopDepth > 0, "linear loop control outside loop"; |
| 13047 | let start = checker.loopMarks[checker.loopDepth - 1]; |
| 13048 | try finishLinearScope(checker, env, start); |
| 13049 | try checkLinearLoopBackEdge(checker, env, node); |
| 13050 | set env.terminated = true; |
| 13051 | } |
| 13052 | case ast::NodeValue::Return { value } => { |
| 13053 | if let expr = value { |
| 13054 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
| 13055 | } |
| 13056 | try finishLinearExit(checker, env); |
| 13057 | } |
| 13058 | case ast::NodeValue::Throw { expr } => { |
| 13059 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
| 13060 | try finishLinearExit(checker, env); |
| 13061 | } |
| 13062 | case ast::NodeValue::Panic { message } => { |
| 13063 | if let expr = message { |
| 13064 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
| 13065 | } |
| 13066 | set env.terminated = true; |
| 13067 | } |
| 13068 | case ast::NodeValue::Assert { condition, message } => { |
| 13069 | try checkLinearNode(checker, env, condition, LinearUse::Consume); |
| 13070 | if let expr = message { |
| 13071 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
| 13072 | } |
| 13073 | } |
| 13074 | else => {} |
| 13075 | } |
| 13076 | } |
| 13077 | |
| 13078 | /// Check exact-use ownership for one resolved function. |
| 13079 | unsafe fn checkLinearFn 'arena ( |
| 13080 | self: &mut Resolver 'arena, |
| 13081 | receiver: ?*ast::Node, |
| 13082 | params: *[*ast::Node], |
| 13083 | body: *ast::Node, |
| 13084 | ) throws (ResolveError) { |
| 13085 | try resolveNominalApplications(self, body); |
| 13086 | let regions = self.regionScope; |
| 13087 | let resolved: 'checking = &mut *self where 'arena: 'checking in { |
| 13088 | let mut checker = linearChecker(resolved, regions); |
| 13089 | let mut env = linearEnv(); |
| 13090 | if let receiverNode = receiver { |
| 13091 | try addLinearBinding(&mut checker, &mut env, receiverNode); |
| 13092 | if let symbol = symbolFor(checker.resolver, receiverNode) { |
| 13093 | if let case SymbolData::Value { |
| 13094 | type: Type::Pointer { |
| 13095 | class: types::PointerClass::Region(permission), |
| 13096 | mutable: true, |
| 13097 | .. |
| 13098 | }, |
| 13099 | .. |
| 13100 | } = symbol.data { |
| 13101 | let index = checker.authorityLen; |
| 13102 | set checker.authorityPermissions[index] = permission; |
| 13103 | set checker.authorityBindings[index] = symbol; |
| 13104 | set checker.authorityExclusive[index] = true; |
| 13105 | set checker.authorityLen += 1; |
| 13106 | } |
| 13107 | } |
| 13108 | } |
| 13109 | for paramNode in params { |
| 13110 | let case ast::NodeValue::FnParam(_) = paramNode.value |
| 13111 | else panic "checkLinearFn: expected parameter"; |
| 13112 | try addLinearBinding(&mut checker, &mut env, paramNode); |
| 13113 | if let symbol = symbolFor(checker.resolver, paramNode) { |
| 13114 | if let case SymbolData::Value { |
| 13115 | type: Type::Pointer { |
| 13116 | class: types::PointerClass::Region(permission), |
| 13117 | mutable: true, |
| 13118 | .. |
| 13119 | }, |
| 13120 | .. |
| 13121 | } = symbol.data { |
| 13122 | if checker.authorityLen >= MAX_LINEAR_BINDINGS { |
| 13123 | throw emitError(checker.resolver, paramNode, ErrorKind::Internal); |
| 13124 | } |
| 13125 | let index = checker.authorityLen; |
| 13126 | set checker.authorityPermissions[index] = permission; |
| 13127 | set checker.authorityBindings[index] = symbol; |
| 13128 | set checker.authorityExclusive[index] = true; |
| 13129 | set checker.authorityLen += 1; |
| 13130 | } |
| 13131 | } |
| 13132 | } |
| 13133 | try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard); |
| 13134 | try finishLinearScope(&mut checker, &mut env, 0); |
| 13135 | } |
| 13136 | } |
| 13137 | |
| 13138 | /// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules. |
| 13139 | unsafe fn resolveModuleDefs 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
| 13140 | for stmt in block.statements { |
| 13141 | try resolveNominalApplications(self, stmt); |
| 13142 | try visitDef(self, stmt); |
| 13143 | try resolveNominalApplications(self, stmt); |
| 13144 | } |
| 13145 | } |
| 13146 | |
| 13147 | /// Resolve all packages. |
| 13148 | /// Module entries retain their identity throughout resolution and diagnostics. |
| 13149 | export unsafe fn resolve 'arena 'permission (self: &mut Resolver 'arena, graph: &module::ModuleGraph 'permission, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
| 13150 | assert module::entryCount(graph) <= self.moduleEntries.len, "resolve: module registry capacity exceeded"; |
| 13151 | for i in 0..self.moduleEntries.len { |
| 13152 | let entry = module::get(graph, i as u16); |
| 13153 | set self.moduleEntries[i] = entry; |
| 13154 | if let present = entry { |
| 13155 | set self.moduleRoots[i] = module::astFor(graph, present); |
| 13156 | } else { |
| 13157 | set self.moduleRoots[i] = nil; |
| 13158 | } |
| 13159 | } |
| 13160 | |
| 13161 | // 1. Bind all package roots to enable cross-package references. |
| 13162 | for i in 0..packages.len { |
| 13163 | let pkg = packages[i]; |
| 13164 | // Enter a new scope for the module. |
| 13165 | let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry); |
| 13166 | // Bind the package root module name in the global package scope. |
| 13167 | let scope = self.pkgScope; |
| 13168 | try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope); |
| 13169 | |
| 13170 | exitModuleScope(self, enter); |
| 13171 | } |
| 13172 | // 2. Resolve each package's contents. |
| 13173 | for i in 0..packages.len { |
| 13174 | let pkg = packages[i]; |
| 13175 | let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst); |
| 13176 | if not success(&diags) { |
| 13177 | return diags; |
| 13178 | } |
| 13179 | } |
| 13180 | return diagnostics(self); |
| 13181 | } |
| 13182 | |
| 13183 | /// Resolve a package. |
| 13184 | unsafe fn resolvePackage 'arena (self: &mut Resolver 'arena, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
| 13185 | let rootId = rootEntry.id; |
| 13186 | let scope = self.moduleScopes[rootId as u32] |
| 13187 | else panic "resolvePackage: module scope not found"; |
| 13188 | |
| 13189 | // Set up the module scope for this package. |
| 13190 | set self.scope = scope; |
| 13191 | set self.currentMod = rootId; |
| 13192 | |
| 13193 | let case ast::NodeValue::Block(block) = node.value |
| 13194 | else panic "resolvePackage: expected block for module root"; |
| 13195 | |
| 13196 | // Module graph analysis phase: bind all module name symbols and scopes. |
| 13197 | try resolveModuleGraph(self, &block) catch { |
| 13198 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
| 13199 | return diagnostics(self); |
| 13200 | }; |
| 13201 | |
| 13202 | // Declaration phase: bind all names and analyze top-level declarations. |
| 13203 | try resolveModuleDecls(self, &block) catch { |
| 13204 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
| 13205 | }; |
| 13206 | if self.errors.len > 0 { |
| 13207 | return diagnostics(self); |
| 13208 | } |
| 13209 | |
| 13210 | // Definition phase: analyze function bodies and sub-module definitions. |
| 13211 | try resolveModuleDefs(self, &block) catch { |
| 13212 | assert self.errors.len > 0, "resolvePackage: failure should have diagnostics"; |
| 13213 | }; |
| 13214 | setNodeType(self, node, Type::Void); |
| 13215 | |
| 13216 | return diagnostics(self); |
| 13217 | } |