compiler: Check identifier ownership through resolved symbol identities
0ed82ea3328ec6b3a3d5462d845276d4c245ac12b34e438843365c0a20d6a1a4
1 parent
cfe03900
lib/std/lang/lower.rad
+22 -22
| 1058 | 1058 | ||
| 1059 | 1059 | // Build qualified function name for multi-module compilation. |
|
| 1060 | 1060 | let qualName = qualifyName(self, nil, name); |
|
| 1061 | 1061 | ||
| 1062 | 1062 | // Register function symbol for cross-package call resolution. |
|
| 1063 | - | if let sym = data.sym { |
|
| 1063 | + | if let sym = resolver::symbolFor(self.resolver, node) { |
|
| 1064 | 1064 | registerSymbolName(self, sym, qualName); |
|
| 1065 | 1065 | } |
|
| 1066 | 1066 | let variableSlots = variableStorage(functionArena, data.localCount); |
|
| 1067 | 1067 | let parent: 'function = &mut *self, arena = &mut *functionArena, variables = &mut variableSlots[..] where 'phase: 'function in { |
|
| 1068 | 1068 | let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena, variables); |
| 1146 | 1146 | traitNameNode: *ast::Node, |
|
| 1147 | 1147 | targetTypeNode: *ast::Node, |
|
| 1148 | 1148 | methods: *[*ast::Node] |
|
| 1149 | 1149 | ) -> InstanceCursor throws (LowerError) where 'arena: 'phase { |
|
| 1150 | 1150 | // Look up the trait and type from the resolver. |
|
| 1151 | - | let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym |
|
| 1151 | + | let traitSym = resolver::symbolFor(self.resolver, traitNameNode) |
|
| 1152 | 1152 | else throw LowerError::MissingSymbol(traitNameNode); |
|
| 1153 | 1153 | let case resolver::SymbolData::Trait(traitInfo) = traitSym.data |
|
| 1154 | 1154 | else throw LowerError::MissingMetadata; |
|
| 1155 | - | let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym |
|
| 1155 | + | let typeSym = resolver::symbolFor(self.resolver, targetTypeNode) |
|
| 1156 | 1156 | else throw LowerError::MissingSymbol(targetTypeNode); |
|
| 1157 | 1157 | ||
| 1158 | 1158 | // Lower each instance method as a regular function. |
|
| 1159 | 1159 | // Collect qualified names for the v-table. Empty entries are filled |
|
| 1160 | 1160 | // later from inherited supertrait methods. |
| 1240 | 1240 | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 1241 | 1241 | let data = resolver::nodeData(self.resolver, node); |
|
| 1242 | 1242 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 1243 | 1243 | throw LowerError::ExpectedFunction; |
|
| 1244 | 1244 | }; |
|
| 1245 | - | let sym = data.sym else throw LowerError::MissingSymbol(node); |
|
| 1245 | + | let sym = resolver::symbolFor(self.resolver, node) else throw LowerError::MissingSymbol(node); |
|
| 1246 | 1246 | registerSymbolName(self, sym, qualName); |
|
| 1247 | 1247 | ||
| 1248 | 1248 | let variableSlots = variableStorage(functionArena, data.localCount); |
|
| 1249 | 1249 | let parent: 'function = &mut *self, arena = &mut *functionArena, variables = &mut variableSlots[..] where 'phase: 'function in { |
|
| 1250 | 1250 | let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena, variables); |
| 1280 | 1280 | receiverName: *ast::Node, |
|
| 1281 | 1281 | sig: ast::FnSig, |
|
| 1282 | 1282 | body: *ast::Node, |
|
| 1283 | 1283 | functionArena: &mut alloc::Arena, |
|
| 1284 | 1284 | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 1285 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1285 | + | let sym = resolver::symbolFor(self.resolver, node) |
|
| 1286 | 1286 | else throw LowerError::MissingSymbol(node); |
|
| 1287 | 1287 | let case ast::NodeValue::Ident(mName) = name.value |
|
| 1288 | 1288 | else throw LowerError::ExpectedIdentifier; |
|
| 1289 | 1289 | let me = resolver::findMethodBySymbol(self.resolver, sym) |
|
| 1290 | 1290 | else throw LowerError::MissingMetadata; |
| 1415 | 1415 | node: *ast::Node, |
|
| 1416 | 1416 | value: *ast::Node, |
|
| 1417 | 1417 | readOnly: bool |
|
| 1418 | 1418 | ) throws (LowerError) where 'arena: 'phase { |
|
| 1419 | 1419 | let data = resolver::nodeData(self.resolver, node); |
|
| 1420 | - | let sym = data.sym else { |
|
| 1420 | + | let sym = resolver::symbolFor(self.resolver, node) else { |
|
| 1421 | 1421 | throw LowerError::MissingSymbol(node); |
|
| 1422 | 1422 | }; |
|
| 1423 | 1423 | if data.ty == resolver::Type::Unknown { |
|
| 1424 | 1424 | throw LowerError::MissingType(node); |
|
| 1425 | 1425 | } |
| 1527 | 1527 | } |
|
| 1528 | 1528 | } |
|
| 1529 | 1529 | } |
|
| 1530 | 1530 | // Function pointer references in constant data. |
|
| 1531 | 1531 | if let case resolver::Type::Fn(_) = ty { |
|
| 1532 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1532 | + | let sym = resolver::symbolFor(self.resolver, node) |
|
| 1533 | 1533 | else throw LowerError::MissingSymbol(node); |
|
| 1534 | 1534 | let modId = resolver::moduleIdForSymbol(self.resolver, sym); |
|
| 1535 | 1535 | let qualName = qualifyName(self, modId, sym.name); |
|
| 1536 | 1536 | dataBuilderPush(b, il::DataValue { |
|
| 1537 | 1537 | item: il::DataItem::Fn(qualName), count: 1, |
|
| 1538 | 1538 | }); |
|
| 1539 | 1539 | return; |
|
| 1540 | 1540 | } |
|
| 1541 | 1541 | // In constant data, a void variant of a mixed union still occupies the |
|
| 1542 | 1542 | // full tagged-union slot. Emitting only the tag corrupts following fields. |
|
| 1543 | - | if let sym = resolver::nodeData(self.resolver, node).sym { |
|
| 1543 | + | if let sym = resolver::symbolFor(self.resolver, node) { |
|
| 1544 | 1544 | if let case resolver::SymbolData::Variant { type: resolver::Type::Void, .. } = sym.data { |
|
| 1545 | 1545 | if let case resolver::Type::Nominal(resolver::NominalType::Union(_)) = ty { |
|
| 1546 | 1546 | try lowerConstUnionVariantInto(self, node, sym, ty, &mut [], dataPrefix, b); |
|
| 1547 | 1547 | return; |
|
| 1548 | 1548 | } |
| 1568 | 1568 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 1569 | 1569 | try lowerConstArrayRepeatInto(self, repeat, ty, dataPrefix, b), |
|
| 1570 | 1570 | case ast::NodeValue::RecordLit(recLit) => |
|
| 1571 | 1571 | try lowerConstRecordLitInto(self, node, recLit, ty, dataPrefix, b), |
|
| 1572 | 1572 | case ast::NodeValue::Call(call) => { |
|
| 1573 | - | let calleeSym = resolver::nodeData(self.resolver, call.callee).sym |
|
| 1573 | + | let calleeSym = resolver::symbolFor(self.resolver, call.callee) |
|
| 1574 | 1574 | else throw LowerError::MissingSymbol(call.callee); |
|
| 1575 | 1575 | match calleeSym.data { |
|
| 1576 | 1576 | case resolver::SymbolData::Variant { .. } => |
|
| 1577 | 1577 | try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b), |
|
| 1578 | 1578 | case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => { |
| 1584 | 1584 | case ast::NodeValue::AddressOf(addr) => { |
|
| 1585 | 1585 | try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b); |
|
| 1586 | 1586 | } |
|
| 1587 | 1587 | case ast::NodeValue::Ident(_) => { |
|
| 1588 | 1588 | // Identifier referencing a constant. |
|
| 1589 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1589 | + | let sym = resolver::symbolFor(self.resolver, node) |
|
| 1590 | 1590 | else throw LowerError::MissingSymbol(node); |
|
| 1591 | 1591 | let case ast::NodeValue::ConstDecl(decl) = sym.node.value |
|
| 1592 | 1592 | else throw LowerError::MissingConst(node); |
|
| 1593 | 1593 | ||
| 1594 | 1594 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1595 | 1595 | }, |
|
| 1596 | 1596 | case ast::NodeValue::ScopeAccess(_) => { |
|
| 1597 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1597 | + | let sym = resolver::symbolFor(self.resolver, node) |
|
| 1598 | 1598 | else throw LowerError::MissingSymbol(node); |
|
| 1599 | 1599 | if let case ast::NodeValue::ConstDecl(decl) = sym.node.value { |
|
| 1600 | 1600 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1601 | 1601 | } else { |
|
| 1602 | 1602 | try lowerConstScalarDataInto(self, node, ty, dataPrefix, b); |
| 1709 | 1709 | } |
|
| 1710 | 1710 | case resolver::Type::Nominal(resolver::NominalType::Union(_)) => { |
|
| 1711 | 1711 | let typeName = recLit.typeName else { |
|
| 1712 | 1712 | throw LowerError::ExpectedVariant; |
|
| 1713 | 1713 | }; |
|
| 1714 | - | let sym = resolver::nodeData(self.resolver, typeName).sym else { |
|
| 1714 | + | let sym = resolver::symbolFor(self.resolver, typeName) else { |
|
| 1715 | 1715 | throw LowerError::MissingSymbol(typeName); |
|
| 1716 | 1716 | }; |
|
| 1717 | 1717 | try lowerConstUnionVariantInto(self, node, sym, ty, recLit.fields, dataPrefix, b); |
|
| 1718 | 1718 | } |
|
| 1719 | 1719 | else => throw LowerError::ExpectedRecord, |
| 2037 | 2037 | return try nextDeclDataName(self.low, fnName, counter, "literal"); |
|
| 2038 | 2038 | } |
|
| 2039 | 2039 | ||
| 2040 | 2040 | /// Assign a unique function-local data symbol name. |
|
| 2041 | 2041 | unsafe fn registerLocalDataDeclName 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2042 | - | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 2042 | + | let sym = resolver::symbolFor(self.low.resolver, node) |
|
| 2043 | 2043 | else throw LowerError::MissingSymbol(node); |
|
| 2044 | 2044 | ||
| 2045 | 2045 | let prefix = self.fnName; |
|
| 2046 | 2046 | let segments = [prefix, "nominal", sym.name]; |
|
| 2047 | 2047 | let name = try buildSegmentedName(self.low, &segments[..]); |
| 2064 | 2064 | return ty; |
|
| 2065 | 2065 | } |
|
| 2066 | 2066 | ||
| 2067 | 2067 | /// Look up the symbol for an AST node, or throw `MissingSymbol`. |
|
| 2068 | 2068 | fn symOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2069 | - | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 2069 | + | let sym = resolver::symbolFor(self.low.resolver, node) |
|
| 2070 | 2070 | else throw LowerError::MissingSymbol(node); |
|
| 2071 | 2071 | return sym; |
|
| 2072 | 2072 | } |
|
| 2073 | 2073 | ||
| 2074 | 2074 | /// Remove the last block parameter and its associated variable. |
| 4288 | 4288 | let data = resolver::nodeData(res, node); |
|
| 4289 | 4289 | // Optional equality checks both the presence tag and the union value. |
|
| 4290 | 4290 | if let case resolver::Coercion::OptionalLift(_) = data.coercion { |
|
| 4291 | 4291 | return nil; |
|
| 4292 | 4292 | } |
|
| 4293 | - | let sym = data.sym else { |
|
| 4293 | + | let sym = resolver::symbolFor(res, node) else { |
|
| 4294 | 4294 | return nil; |
|
| 4295 | 4295 | }; |
|
| 4296 | 4296 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
|
| 4297 | 4297 | return nil; |
|
| 4298 | 4298 | }; |
| 5202 | 5202 | ||
| 5203 | 5203 | return il::Val::Reg(ptr); |
|
| 5204 | 5204 | } |
|
| 5205 | 5205 | // A qualified constant or static uses its resolved owner's data symbol. |
|
| 5206 | 5206 | if let case ast::NodeValue::ScopeAccess(_) = addr.target.value { |
|
| 5207 | - | let sym = resolver::nodeData(self.low.resolver, addr.target).sym else { |
|
| 5207 | + | let sym = resolver::symbolFor(self.low.resolver, addr.target) else { |
|
| 5208 | 5208 | throw LowerError::MissingSymbol(addr.target); |
|
| 5209 | 5209 | }; |
|
| 5210 | 5210 | return il::Val::Reg(emitDataAddr(self, sym)); |
|
| 5211 | 5211 | } |
|
| 5212 | 5212 | // Handle variable address: `&x` |
| 5235 | 5235 | defVar(self, v, stackVal); |
|
| 5236 | 5236 | ||
| 5237 | 5237 | return stackVal; |
|
| 5238 | 5238 | } |
|
| 5239 | 5239 | // Fall back to symbol lookup for constants/statics. |
|
| 5240 | - | if let sym = resolver::nodeData(self.low.resolver, addr.target).sym { |
|
| 5240 | + | if let sym = resolver::symbolFor(self.low.resolver, addr.target) { |
|
| 5241 | 5241 | return il::Val::Reg(emitDataAddr(self, sym)); |
|
| 5242 | 5242 | } else { |
|
| 5243 | 5243 | throw LowerError::MissingSymbol(node); |
|
| 5244 | 5244 | } |
|
| 5245 | 5245 | } |
| 5415 | 5415 | // If the resolver determined that this variable's address is taken |
|
| 5416 | 5416 | // anywhere in the function, allocate a stack slot immediately so the |
|
| 5417 | 5417 | // SSA value is always a pointer. This avoids mixing integer and pointer |
|
| 5418 | 5418 | // values in loop phis when `&var` or `&mut var` appears inside a loop. |
|
| 5419 | 5419 | if not isAggregateType(typ) { |
|
| 5420 | - | if let sym = resolver::nodeData(self.low.resolver, node).sym { |
|
| 5420 | + | if let sym = resolver::symbolFor(self.low.resolver, node) { |
|
| 5421 | 5421 | if let case resolver::SymbolData::Value { addressTaken, .. } = sym.data; addressTaken { |
|
| 5422 | 5422 | let layout = resolver::getLayout(self.low.resolver, node, typ); |
|
| 5423 | 5423 | let slot = emitReserveLayout(self, layout); |
|
| 5424 | 5424 | try emitStore(self, slot, 0, typ, varVal); |
|
| 5425 | 5425 |
| 6963 | 6963 | } |
|
| 6964 | 6964 | // Check for standalone method call. |
|
| 6965 | 6965 | if let case resolver::NodeExtra::MethodCall { method } = nodeData { |
|
| 6966 | 6966 | return try lowerMethodCall(self, node, call, method); |
|
| 6967 | 6967 | } |
|
| 6968 | - | if let sym = resolver::nodeData(self.low.resolver, call.callee).sym { |
|
| 6968 | + | if let sym = resolver::symbolFor(self.low.resolver, call.callee) { |
|
| 6969 | 6969 | if let case resolver::SymbolData::Type(_) = sym.data { |
|
| 6970 | 6970 | let ty = try typeOf(self, node); |
|
| 6971 | 6971 | let case resolver::Type::Nominal(nominal) = ty else throw LowerError::ExpectedRecord; |
|
| 6972 | 6972 | return try lowerRecordCtor(self, nominal, call.args); |
|
| 6973 | 6973 | } |
| 7189 | 7189 | } |
|
| 7190 | 7190 | ||
| 7191 | 7191 | /// Check if a call is to a compiler intrinsic and lower it directly. |
|
| 7192 | 7192 | unsafe fn lowerIntrinsicCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> ?il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7193 | 7193 | // Get the callee symbol and check if it's marked as an intrinsic. |
|
| 7194 | - | let sym = resolver::nodeData(self.low.resolver, call.callee).sym else { |
|
| 7194 | + | let sym = resolver::symbolFor(self.low.resolver, call.callee) else { |
|
| 7195 | 7195 | // Expressions or function pointers may not have an associated symbol. |
|
| 7196 | 7196 | return nil; |
|
| 7197 | 7197 | }; |
|
| 7198 | 7198 | if not ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic) { |
|
| 7199 | 7199 | return nil; |
| 7264 | 7264 | ||
| 7265 | 7265 | /// Resolve callee to an IL value. For direct function calls, use the symbol name. |
|
| 7266 | 7266 | /// For variables holding function pointers or complex expressions (eg. `array[i]()`), |
|
| 7267 | 7267 | /// lower the callee expression. |
|
| 7268 | 7268 | unsafe fn lowerCallee 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, callee: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7269 | - | if let sym = resolver::nodeData(self.low.resolver, callee).sym { |
|
| 7269 | + | if let sym = resolver::symbolFor(self.low.resolver, callee) { |
|
| 7270 | 7270 | if let case ast::NodeValue::FnDecl(_) = sym.node.value { |
|
| 7271 | 7271 | // First try to look up the symbol in our registered functions. |
|
| 7272 | 7272 | // This handles cross-package calls correctly, since packages are |
|
| 7273 | 7273 | // lowered in dependency order. |
|
| 7274 | 7274 | if let qualName = lookupSymbolName(self.low, sym.id) { |
| 7409 | 7409 | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7410 | 7410 | return try constValueToVal(self, constVal, node); |
|
| 7411 | 7411 | } |
|
| 7412 | 7412 | // Otherwise get the associated symbol. |
|
| 7413 | 7413 | let data = resolver::nodeData(self.low.resolver, node); |
|
| 7414 | - | let sym = data.sym else { |
|
| 7414 | + | let sym = resolver::symbolFor(self.low.resolver, node) else { |
|
| 7415 | 7415 | throw LowerError::MissingSymbol(node); |
|
| 7416 | 7416 | }; |
|
| 7417 | 7417 | match sym.data { |
|
| 7418 | 7418 | case resolver::SymbolData::Variant { index, .. } => { |
|
| 7419 | 7419 | let mut indexValue = index as i64; |
lib/std/lang/resolver.rad
+28 -20
| 789 | 789 | SliceAppend { elemType: *Type }, |
|
| 790 | 790 | /// Slice `.delete(index)` method call. |
|
| 791 | 791 | SliceDelete { elemType: *Type }, |
|
| 792 | 792 | } |
|
| 793 | 793 | ||
| 794 | + | /// Symbol identity and storage associated with a resolved AST node. |
|
| 795 | + | export record ResolvedSymbol: Copy { |
|
| 796 | + | /// Identity within the resolver that owns the node metadata. |
|
| 797 | + | id: u32, |
|
| 798 | + | /// Symbol storage used by type resolution and lowering. |
|
| 799 | + | symbol: *unsafe mut Symbol, |
|
| 800 | + | } |
|
| 801 | + | ||
| 794 | 802 | /// Combined resolver metadata for a single AST node. |
|
| 795 | 803 | export record NodeData: Copy { |
|
| 796 | 804 | /// Number of local bindings and internal iteration variables in this function. |
|
| 797 | 805 | localCount: u32, |
|
| 798 | 806 | /// Resolved type for this node. |
|
| 799 | 807 | ty: Type, |
|
| 800 | 808 | /// Coercion plan applied to this node. |
|
| 801 | 809 | coercion: Coercion, |
|
| 802 | - | /// Symbol associated with this node. |
|
| 803 | - | sym: ?*unsafe mut Symbol, |
|
| 810 | + | /// Symbol identity and storage associated with this node. |
|
| 811 | + | binding: ?ResolvedSymbol, |
|
| 804 | 812 | /// Constant value for literal nodes. |
|
| 805 | 813 | constValue: ?ConstValue, |
|
| 806 | 814 | /// Lexical scope owned by this node. |
|
| 807 | 815 | scope: ?*unsafe mut Scope, |
|
| 808 | 816 | /// Node-specific extra data. |
| 1511 | 1519 | for i in 0..nodeData.len { |
|
| 1512 | 1520 | set nodeData[i] = NodeData { |
|
| 1513 | 1521 | localCount: 0, |
|
| 1514 | 1522 | ty: Type::Unknown, |
|
| 1515 | 1523 | coercion: Coercion::Identity, |
|
| 1516 | - | sym: nil, |
|
| 1524 | + | binding: nil, |
|
| 1517 | 1525 | constValue: nil, |
|
| 1518 | 1526 | scope: nil, |
|
| 1519 | 1527 | extra: NodeExtra::None, |
|
| 1520 | 1528 | }; |
|
| 1521 | 1529 | } |
| 1744 | 1752 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 1745 | 1753 | return name; |
|
| 1746 | 1754 | } |
|
| 1747 | 1755 | ||
| 1748 | 1756 | /// Associate a resolved symbol with an AST node. |
|
| 1749 | - | fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) { |
|
| 1750 | - | if let existingSym = self.nodeData.entries[node.id].sym { |
|
| 1757 | + | unsafe fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) { |
|
| 1758 | + | if let existingSym = self.nodeData.entries[node.id].binding { |
|
| 1751 | 1759 | panic "setNodeSymbol: a symbol is already associated with this node"; |
|
| 1752 | 1760 | } |
|
| 1753 | - | set self.nodeData.entries[node.id].sym = symbol; |
|
| 1761 | + | set self.nodeData.entries[node.id].binding = ResolvedSymbol { id: symbol.id, symbol }; |
|
| 1754 | 1762 | } |
|
| 1755 | 1763 | ||
| 1756 | 1764 | /// Associate a resolved type with an AST node and return it. |
|
| 1757 | 1765 | fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type { |
|
| 1758 | 1766 | if ty == Type::Unknown { |
| 1890 | 1898 | return self.nodeData.entries[node.id].scope; |
|
| 1891 | 1899 | } |
|
| 1892 | 1900 | ||
| 1893 | 1901 | /// Get the symbol bound to a node. |
|
| 1894 | 1902 | export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 1895 | - | return self.nodeData.entries[node.id].sym; |
|
| 1903 | + | let binding = self.nodeData.entries[node.id].binding else return nil; |
|
| 1904 | + | return binding.symbol; |
|
| 1896 | 1905 | } |
|
| 1897 | 1906 | ||
| 1898 | 1907 | /// Get the coercion plan associated with a node, if any. |
|
| 1899 | 1908 | export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion { |
|
| 1900 | 1909 | let c = self.nodeData.entries[node.id].coercion; |
| 8367 | 8376 | } |
|
| 8368 | 8377 | case ast::NodeValue::ScopeAccess(_) => { |
|
| 8369 | 8378 | // Module-qualified access to a top-level symbol. A `static` |
|
| 8370 | 8379 | // binds as a mutable value; a `constant` does not. |
|
| 8371 | 8380 | let _ = try infer(self, node); |
|
| 8372 | - | let sym = nodeData(self, node).sym |
|
| 8381 | + | let sym = symbolFor(self, node) |
|
| 8373 | 8382 | else return false; |
|
| 8374 | 8383 | ||
| 8375 | 8384 | if let case SymbolData::Value { mutable, .. } = sym.data { |
|
| 8376 | 8385 | return mutable; |
|
| 8377 | 8386 | } |
| 9934 | 9943 | set env.available |= (1 as u64) << (env.len as u64); |
|
| 9935 | 9944 | set env.len += 1; |
|
| 9936 | 9945 | } |
|
| 9937 | 9946 | ||
| 9938 | 9947 | /// Mark a tracked binding as uninitialized. |
|
| 9939 | - | unsafe fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) { |
|
| 9940 | - | let sym = symbolFor(self, node) else return; |
|
| 9941 | - | let index = findLinearBinding(env, sym.id) else return; |
|
| 9948 | + | fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) { |
|
| 9949 | + | let binding = self.nodeData.entries[node.id].binding else return; |
|
| 9950 | + | let index = findLinearBinding(env, binding.id) else return; |
|
| 9942 | 9951 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 9943 | 9952 | } |
|
| 9944 | 9953 | ||
| 9945 | 9954 | /// Require exact-use bindings introduced after `start` to be consumed. |
|
| 9946 | 9955 | fn finishLinearScope 'arena 'checking ( |
| 9964 | 9973 | } |
|
| 9965 | 9974 | set env.len = start; |
|
| 9966 | 9975 | } |
|
| 9967 | 9976 | ||
| 9968 | 9977 | /// Require a tracked identifier to remain available for any access. |
|
| 9969 | - | unsafe fn checkLinearIdent 'arena 'checking ( |
|
| 9978 | + | fn checkLinearIdent 'arena 'checking ( |
|
| 9970 | 9979 | checker: &mut LinearChecker 'arena 'checking, |
|
| 9971 | 9980 | env: &mut LinearEnv, |
|
| 9972 | 9981 | node: *ast::Node, |
|
| 9973 | 9982 | ) throws (ResolveError) where 'arena: 'checking { |
|
| 9974 | - | let sym = symbolFor(checker.resolver, node) else return; |
|
| 9975 | - | let index = findLinearBinding(env, sym.id) else return; |
|
| 9983 | + | let binding = checker.resolver.nodeData.entries[node.id].binding else return; |
|
| 9984 | + | let index = findLinearBinding(env, binding.id) else return; |
|
| 9976 | 9985 | if not linearBindingAvailable(env, index) { |
|
| 9977 | - | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 9978 | - | else panic "consumeLinearIdent: expected value symbol"; |
|
| 9979 | - | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty) |
|
| 9986 | + | let sym = env.symbols[index]; |
|
| 9987 | + | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if sym.usage == BindingUse::Linear |
|
| 9980 | 9988 | else ErrorKind::AffineUseAfterMove(sym.name); |
|
| 9981 | 9989 | throw emitError(checker.resolver, node, kind); |
|
| 9982 | 9990 | } |
|
| 9983 | 9991 | } |
|
| 9984 | 9992 | ||
| 9985 | 9993 | /// Move or consume a tracked identifier once. |
|
| 9986 | - | unsafe fn consumeLinearIdent 'arena 'checking ( |
|
| 9994 | + | fn consumeLinearIdent 'arena 'checking ( |
|
| 9987 | 9995 | checker: &mut LinearChecker 'arena 'checking, |
|
| 9988 | 9996 | env: &mut LinearEnv, |
|
| 9989 | 9997 | node: *ast::Node, |
|
| 9990 | 9998 | ) throws (ResolveError) where 'arena: 'checking { |
|
| 9991 | 9999 | try checkLinearIdent(checker, env, node); |
|
| 9992 | - | let sym = symbolFor(checker.resolver, node) else return; |
|
| 9993 | - | let index = findLinearBinding(env, sym.id) else return; |
|
| 10000 | + | let binding = checker.resolver.nodeData.entries[node.id].binding else return; |
|
| 10001 | + | let index = findLinearBinding(env, binding.id) else return; |
|
| 9994 | 10002 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 9995 | 10003 | } |
|
| 9996 | 10004 | ||
| 9997 | 10005 | /// Merge ownership availability across two live branches. |
|
| 9998 | 10006 | /// Validate both inputs before writing to an output that can alias either input. |
lib/std/lang/resolver/tests/regions.rad
+4 -0
| 17 | 17 | let first = resolver::symbolFor(&res, block.statements[0]) else throw testing::TestError::Failed; |
|
| 18 | 18 | let second = resolver::symbolFor(&res, block.statements[1]) else throw testing::TestError::Failed; |
|
| 19 | 19 | assert first.id <> second.id; |
|
| 20 | 20 | assert first.id < res.symbolCount; |
|
| 21 | 21 | assert second.id < res.symbolCount; |
|
| 22 | + | let firstBinding = resolver::nodeData(&res, block.statements[0]).binding else throw testing::TestError::Failed; |
|
| 23 | + | let secondBinding = resolver::nodeData(&res, block.statements[1]).binding else throw testing::TestError::Failed; |
|
| 24 | + | assert firstBinding.id == first.id; |
|
| 25 | + | assert secondBinding.id == second.id; |
|
| 22 | 26 | } |
|
| 23 | 27 | } |
|
| 24 | 28 | ||
| 25 | 29 | /// Short-circuit paths must agree on exact-use regional ownership. |
|
| 26 | 30 | @test unsafe fn testRegionalShortCircuitOwnership() throws (testing::TestError) { |