lang: Parse linear and pointer types
8ed9e8cc40e335d07b37836be7c4d3e7d53c243a3dfb968507a20122b6bfab7a
1 parent
6bcd1af1
lib/std/lang.rad
+1 -0
| 1 | 1 | //! Radiance language implementation. |
|
| 2 | 2 | export mod alloc; |
|
| 3 | 3 | export mod sexpr; |
|
| 4 | + | export mod types; |
|
| 4 | 5 | export mod strings; |
|
| 5 | 6 | export mod scanner; |
|
| 6 | 7 | export mod ast; |
|
| 7 | 8 | export mod parser; |
|
| 8 | 9 | export mod module; |
lib/std/lang/ast.rad
+15 -3
| 2 | 2 | export mod printer; |
|
| 3 | 3 | ||
| 4 | 4 | use std::io; |
|
| 5 | 5 | use std::fmt; |
|
| 6 | 6 | use std::lang::alloc; |
|
| 7 | + | use std::lang::types; |
|
| 7 | 8 | ||
| 8 | 9 | /// Maximum number of trait methods. |
|
| 9 | 10 | export constant MAX_TRAIT_METHODS: u32 = 8; |
|
| 10 | 11 | ||
| 11 | 12 | /// Arena for all parser allocations. |
| 46 | 47 | Extern = 0b100, |
|
| 47 | 48 | /// Test-only declaration attribute. |
|
| 48 | 49 | Test = 0b1000, |
|
| 49 | 50 | /// Compiler intrinsic attribute. |
|
| 50 | 51 | Intrinsic = 0b10000, |
|
| 52 | + | /// Declaration may perform unsafe pointer operations. |
|
| 53 | + | Unsafe = 0b100000, |
|
| 51 | 54 | } |
|
| 52 | 55 | ||
| 53 | 56 | /// Ordered collection of attribute nodes applied to a declaration. |
|
| 54 | 57 | export record Attributes { |
|
| 55 | 58 | list: *mut [*Node], |
| 170 | 173 | /// Array element type. |
|
| 171 | 174 | itemType: *Node, |
|
| 172 | 175 | /// Expression that evaluates to the array length. |
|
| 173 | 176 | length: *Node, |
|
| 174 | 177 | }, |
|
| 175 | - | /// Slice type, eg. `*[i32]` or `*mut [i32]`. |
|
| 178 | + | /// Slice type, eg. `*[i32]`, `&[i32]`, or `*unsafe [i32]`. |
|
| 176 | 179 | Slice { |
|
| 180 | + | /// Ownership and safety class. |
|
| 181 | + | class: types::PointerClass, |
|
| 177 | 182 | /// Slice element type. |
|
| 178 | 183 | itemType: *Node, |
|
| 179 | 184 | /// Whether the slice is mutable. |
|
| 180 | 185 | mutable: bool, |
|
| 181 | 186 | }, |
|
| 182 | - | /// Pointer, eg. `*i32` or `*mut i32`. |
|
| 187 | + | /// Pointer type, eg. `*i32`, `&i32`, or `*unsafe i32`. |
|
| 183 | 188 | Pointer { |
|
| 189 | + | /// Ownership and safety class. |
|
| 190 | + | class: types::PointerClass, |
|
| 184 | 191 | /// Pointer target type. |
|
| 185 | 192 | valueType: *Node, |
|
| 186 | 193 | /// Whether the pointer is mutable. |
|
| 187 | 194 | mutable: bool, |
|
| 188 | 195 | }, |
| 200 | 207 | /// Whether this record has labeled fields. |
|
| 201 | 208 | labeled: bool, |
|
| 202 | 209 | }, |
|
| 203 | 210 | /// Anonymous function type. |
|
| 204 | 211 | Fn(FnSig), |
|
| 205 | - | /// Trait object type, eg. `*opaque Allocator`. |
|
| 212 | + | /// Trait object type, eg. `*opaque Allocator`, `&opaque Allocator`, or |
|
| 213 | + | /// `*unsafe opaque Allocator`. |
|
| 206 | 214 | TraitObject { |
|
| 215 | + | /// Ownership and safety class. |
|
| 216 | + | class: types::PointerClass, |
|
| 207 | 217 | /// Trait name identifier. |
|
| 208 | 218 | traitName: *Node, |
|
| 209 | 219 | /// Whether the pointer is mutable. |
|
| 210 | 220 | mutable: bool, |
|
| 211 | 221 | }, |
| 752 | 762 | name: *Node, |
|
| 753 | 763 | /// Receiver type node (eg. `*mut Allocator`). |
|
| 754 | 764 | receiver: *Node, |
|
| 755 | 765 | /// Function signature. |
|
| 756 | 766 | sig: FnSig, |
|
| 767 | + | /// Optional declaration modifiers. |
|
| 768 | + | attrs: ?Attributes, |
|
| 757 | 769 | }, |
|
| 758 | 770 | /// Instance block. |
|
| 759 | 771 | InstanceDecl { |
|
| 760 | 772 | /// Trait name identifier. |
|
| 761 | 773 | traitName: *Node, |
lib/std/lang/ast/printer.rad
+64 -13
| 1 | 1 | //! AST pretty printer using S-expression syntax. |
|
| 2 | 2 | ||
| 3 | 3 | use std::io; |
|
| 4 | 4 | use std::lang::sexpr; |
|
| 5 | 5 | use std::lang::alloc; |
|
| 6 | + | use std::lang::types; |
|
| 6 | 7 | ||
| 7 | 8 | /// Return the symbol for a binary operator. |
|
| 8 | 9 | fn binOpName(op: super::BinaryOp) -> *[u8] { |
|
| 9 | 10 | match op { |
|
| 10 | 11 | case super::BinaryOp::Add => return "+", |
| 66 | 67 | else => panic, |
|
| 67 | 68 | } |
|
| 68 | 69 | } |
|
| 69 | 70 | } |
|
| 70 | 71 | ||
| 72 | + | /// Return the S-expression head for a pointer class. |
|
| 73 | + | fn pointerClassHead( |
|
| 74 | + | class: types::PointerClass, |
|
| 75 | + | ownedHead: *[u8], |
|
| 76 | + | refHead: *[u8], |
|
| 77 | + | unsafeHead: *[u8], |
|
| 78 | + | ) -> *[u8] { |
|
| 79 | + | match class { |
|
| 80 | + | case types::PointerClass::Owned => return ownedHead, |
|
| 81 | + | case types::PointerClass::Ref => return refHead, |
|
| 82 | + | case types::PointerClass::Unsafe => return unsafeHead, |
|
| 83 | + | } |
|
| 84 | + | } |
|
| 85 | + | ||
| 71 | 86 | /// Convert a type signature to an S-expression. |
|
| 72 | 87 | fn typeSigToExpr(a: *mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr { |
|
| 73 | 88 | match sig { |
|
| 74 | 89 | case super::TypeSig::Void => return sexpr::sym("void"), |
|
| 75 | 90 | case super::TypeSig::Opaque => return sexpr::sym("opaque"), |
|
| 76 | 91 | case super::TypeSig::Bool => return sexpr::sym("bool"), |
|
| 77 | 92 | case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)), |
|
| 78 | 93 | case super::TypeSig::Array { itemType, length } => |
|
| 79 | 94 | return sexpr::list(a, "array", &[toExpr(a, itemType), toExpr(a, length)]), |
|
| 80 | - | case super::TypeSig::Slice { itemType, mutable } => |
|
| 81 | - | return sexpr::list(a, "slice", &[sexpr::sym("mut"), toExpr(a, itemType)]) if mutable |
|
| 82 | - | else sexpr::list(a, "slice", &[toExpr(a, itemType)]), |
|
| 83 | - | case super::TypeSig::Pointer { valueType, mutable } => |
|
| 84 | - | return sexpr::list(a, "ptr", &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable |
|
| 85 | - | else sexpr::list(a, "ptr", &[toExpr(a, valueType)]), |
|
| 95 | + | case super::TypeSig::Slice { class, itemType, mutable } => { |
|
| 96 | + | let head = pointerClassHead(class, "slice", "slice-ref", "unsafe-slice"); |
|
| 97 | + | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, itemType)]) if mutable |
|
| 98 | + | else sexpr::list(a, head, &[toExpr(a, itemType)]); |
|
| 99 | + | } |
|
| 100 | + | case super::TypeSig::Pointer { class, valueType, mutable } => { |
|
| 101 | + | let head = pointerClassHead(class, "ptr", "ref", "unsafe-ptr"); |
|
| 102 | + | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable |
|
| 103 | + | else sexpr::list(a, head, &[toExpr(a, valueType)]); |
|
| 104 | + | } |
|
| 86 | 105 | case super::TypeSig::Optional { valueType } => |
|
| 87 | 106 | return sexpr::list(a, "?", &[toExpr(a, valueType)]), |
|
| 88 | 107 | case super::TypeSig::Nominal(name) => return toExpr(a, name), |
|
| 89 | 108 | case super::TypeSig::Record { fields, .. } => |
|
| 90 | 109 | return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])), |
| 93 | 112 | if let rt = sig.returnType { |
|
| 94 | 113 | set ret = toExpr(a, rt); |
|
| 95 | 114 | } |
|
| 96 | 115 | return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]); |
|
| 97 | 116 | } |
|
| 98 | - | case super::TypeSig::TraitObject { traitName, mutable } => |
|
| 99 | - | return sexpr::list(a, "obj", &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable |
|
| 100 | - | else sexpr::list(a, "obj", &[toExpr(a, traitName)]), |
|
| 117 | + | case super::TypeSig::TraitObject { class, traitName, mutable } => { |
|
| 118 | + | let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj"); |
|
| 119 | + | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable |
|
| 120 | + | else sexpr::list(a, head, &[toExpr(a, traitName)]); |
|
| 121 | + | } |
|
| 101 | 122 | } |
|
| 102 | 123 | } |
|
| 103 | 124 | ||
| 104 | 125 | /// Convert a node slice to a slice of expressions. |
|
| 105 | 126 | fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
| 111 | 132 | set buf[i] = toExpr(a, node); |
|
| 112 | 133 | } |
|
| 113 | 134 | return buf; |
|
| 114 | 135 | } |
|
| 115 | 136 | ||
| 137 | + | /// Convert optional attributes to an attribute list expression. |
|
| 138 | + | fn attributesToExpr(a: *mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr { |
|
| 139 | + | let mut exprs: *[sexpr::Expr] = &[]; |
|
| 140 | + | if let list = attrs { |
|
| 141 | + | set exprs = nodeListToExprs(a, &list.list[..]); |
|
| 142 | + | } |
|
| 143 | + | return sexpr::list(a, "attrs", exprs); |
|
| 144 | + | } |
|
| 145 | + | ||
| 116 | 146 | /// Convert an optional node to an expression, or return placeholder. |
|
| 117 | 147 | fn toExprOpt(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr { |
|
| 118 | 148 | if let n = opt { |
|
| 119 | 149 | return toExpr(a, n); |
|
| 120 | 150 | } |
| 305 | 335 | case super::Attribute::Export => return sexpr::sym("@export"), |
|
| 306 | 336 | case super::Attribute::Default => return sexpr::sym("@default"), |
|
| 307 | 337 | case super::Attribute::Extern => return sexpr::sym("@extern"), |
|
| 308 | 338 | case super::Attribute::Test => return sexpr::sym("@test"), |
|
| 309 | 339 | case super::Attribute::Intrinsic => return sexpr::sym("@intrinsic"), |
|
| 340 | + | case super::Attribute::Unsafe => return sexpr::sym("@unsafe"), |
|
| 310 | 341 | } |
|
| 311 | 342 | } |
|
| 312 | 343 | case super::NodeValue::Try(t) => { |
|
| 313 | 344 | let mut head = "try"; |
|
| 314 | 345 | if t.shouldPanic { set head = "try!"; } |
| 444 | 475 | case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => { |
|
| 445 | 476 | let children = nodeListToExprs(a, &methods[..]); |
|
| 446 | 477 | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, &supertraits[..])); |
|
| 447 | 478 | return sexpr::block(a, "trait", &[toExpr(a, name), supers], children); |
|
| 448 | 479 | } |
|
| 449 | - | case super::NodeValue::TraitMethodSig { name, receiver, sig } => { |
|
| 480 | + | case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => { |
|
| 450 | 481 | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 451 | 482 | let ret = toExprOrNull(a, sig.returnType); |
|
| 452 | - | return sexpr::list(a, "methodSig", &[toExpr(a, receiver), toExpr(a, name), params, ret]); |
|
| 483 | + | let attributes = attributesToExpr(a, attrs); |
|
| 484 | + | return sexpr::list( |
|
| 485 | + | a, |
|
| 486 | + | "methodSig", |
|
| 487 | + | &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret], |
|
| 488 | + | ); |
|
| 453 | 489 | } |
|
| 454 | 490 | case super::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 455 | 491 | let children = nodeListToExprs(a, &methods[..]); |
|
| 456 | 492 | return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children); |
|
| 457 | 493 | } |
|
| 458 | - | case super::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, .. } => { |
|
| 494 | + | case super::NodeValue::MethodDecl { |
|
| 495 | + | name, receiverName, receiverType, sig, body, attrs, |
|
| 496 | + | } => { |
|
| 459 | 497 | let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])); |
|
| 460 | 498 | let ret = toExprOrNull(a, sig.returnType); |
|
| 461 | - | return sexpr::block(a, "method", &[toExpr(a, receiverType), toExpr(a, receiverName), toExpr(a, name), params, ret], &[toExpr(a, body)]); |
|
| 499 | + | let attributes = attributesToExpr(a, attrs); |
|
| 500 | + | return sexpr::block( |
|
| 501 | + | a, |
|
| 502 | + | "method", |
|
| 503 | + | &[ |
|
| 504 | + | attributes, |
|
| 505 | + | toExpr(a, receiverType), |
|
| 506 | + | toExpr(a, receiverName), |
|
| 507 | + | toExpr(a, name), |
|
| 508 | + | params, |
|
| 509 | + | ret, |
|
| 510 | + | ], |
|
| 511 | + | &[toExpr(a, body)], |
|
| 512 | + | ); |
|
| 462 | 513 | } |
|
| 463 | 514 | else => return sexpr::sym("?"), |
|
| 464 | 515 | } |
|
| 465 | 516 | } |
|
| 466 | 517 |
lib/std/lang/lower.rad
+16 -9
| 88 | 88 | //! il::Data |
|
| 89 | 89 | //! |
|
| 90 | 90 | use std::fmt; |
|
| 91 | 91 | use std::io; |
|
| 92 | 92 | use std::lang::alloc; |
|
| 93 | + | use std::lang::types; |
|
| 93 | 94 | use std::mem; |
|
| 94 | 95 | use std::lang::ast; |
|
| 95 | 96 | use std::lang::il; |
|
| 96 | 97 | use std::lang::module; |
|
| 97 | 98 | use std::lang::resolver; |
| 4220 | 4221 | fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4221 | 4222 | match optType { |
|
| 4222 | 4223 | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4223 | 4224 | return il::Val::Imm(0); |
|
| 4224 | 4225 | } |
|
| 4225 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => { |
|
| 4226 | + | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4226 | 4227 | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4227 | 4228 | } |
|
| 4228 | 4229 | case resolver::Type::Optional(inner) => { |
|
| 4229 | 4230 | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4230 | 4231 | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
| 4254 | 4255 | mutable: bool, |
|
| 4255 | 4256 | ptrVal: il::Val, |
|
| 4256 | 4257 | lenVal: il::Val, |
|
| 4257 | 4258 | capVal: il::Val |
|
| 4258 | 4259 | ) -> il::Val throws (LowerError) { |
|
| 4259 | - | let sliceType = resolver::Type::Slice { item: elemTy, mutable }; |
|
| 4260 | + | let sliceType = resolver::Type::Slice { |
|
| 4261 | + | class: types::PointerClass::Owned, item: elemTy, mutable, |
|
| 4262 | + | }; |
|
| 4260 | 4263 | let dst = try emitReserve(self, sliceType); |
|
| 4261 | - | let ptrTy = resolver::Type::Pointer { target: elemTy, mutable }; |
|
| 4264 | + | let ptrTy = resolver::Type::Pointer { |
|
| 4265 | + | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4266 | + | }; |
|
| 4262 | 4267 | ||
| 4263 | 4268 | try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal); |
|
| 4264 | 4269 | try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal); |
|
| 4265 | 4270 | try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal); |
|
| 4266 | 4271 |
| 4444 | 4449 | mutable: bool, |
|
| 4445 | 4450 | a: il::Reg, |
|
| 4446 | 4451 | b: il::Reg, |
|
| 4447 | 4452 | offset: i32 |
|
| 4448 | 4453 | ) -> il::Val throws (LowerError) { |
|
| 4449 | - | let ptrTy = resolver::Type::Pointer { target: elemTy, mutable }; |
|
| 4454 | + | let ptrTy = resolver::Type::Pointer { |
|
| 4455 | + | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4456 | + | }; |
|
| 4450 | 4457 | let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy); |
|
| 4451 | 4458 | let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32); |
|
| 4452 | 4459 | ||
| 4453 | 4460 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq); |
|
| 4454 | 4461 | } |
| 4693 | 4700 | a: il::Reg, |
|
| 4694 | 4701 | b: il::Reg, |
|
| 4695 | 4702 | offset: i32 |
|
| 4696 | 4703 | ) -> il::Val throws (LowerError) { |
|
| 4697 | 4704 | match typ { |
|
| 4698 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => { |
|
| 4705 | + | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4699 | 4706 | // Optional slices use null pointer optimization. |
|
| 4700 | 4707 | return try lowerSliceEq(self, item, mutable, a, b, offset); |
|
| 4701 | 4708 | } |
|
| 4702 | 4709 | case resolver::Type::Optional(inner) => { |
|
| 4703 | 4710 | return try lowerOptionalEq(self, *inner, a, b, offset); |
|
| 4704 | 4711 | } |
|
| 4705 | - | case resolver::Type::Slice { item, mutable } => |
|
| 4712 | + | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4706 | 4713 | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4707 | 4714 | case resolver::Type::Array(arr) => |
|
| 4708 | 4715 | return try lowerArrayEq(self, arr, a, b, offset), |
|
| 4709 | 4716 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => |
|
| 4710 | 4717 | return try lowerRecordEq(self, recInfo, a, b, offset), |
| 5058 | 5065 | self: *mut FnLowerer, |
|
| 5059 | 5066 | sliceNode: *ast::Node, |
|
| 5060 | 5067 | arrayNode: *ast::Node |
|
| 5061 | 5068 | ) -> il::Val throws (LowerError) { |
|
| 5062 | 5069 | let sliceTy = try typeOf(self, sliceNode); |
|
| 5063 | - | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 5070 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 5064 | 5071 | throw LowerError::UnexpectedType(&sliceTy); |
|
| 5065 | 5072 | }; |
|
| 5066 | 5073 | let arrayTy = try typeOf(self, arrayNode); |
|
| 5067 | 5074 | let case resolver::Type::Array(arrayInfo) = arrayTy else { |
|
| 5068 | 5075 | throw LowerError::ExpectedArray; |
| 6140 | 6147 | /// String literals are stored as global data and the result is a slice |
|
| 6141 | 6148 | /// pointing to the data with the appropriate length. |
|
| 6142 | 6149 | fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6143 | 6150 | // Get the slice type from the node. |
|
| 6144 | 6151 | let sliceTy = try typeOf(self, node); |
|
| 6145 | - | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 6152 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6146 | 6153 | throw LowerError::ExpectedSliceOrArray; |
|
| 6147 | 6154 | }; |
|
| 6148 | 6155 | // Build the string data value. |
|
| 6149 | 6156 | let ptr = try! alloc::alloc( |
|
| 6150 | 6157 | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
| 6172 | 6179 | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6173 | 6180 | if args.len <> 2 and args.len <> 3 { |
|
| 6174 | 6181 | throw LowerError::InvalidArgCount; |
|
| 6175 | 6182 | } |
|
| 6176 | 6183 | let sliceTy = try typeOf(self, node); |
|
| 6177 | - | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 6184 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6178 | 6185 | throw LowerError::ExpectedSliceOrArray; |
|
| 6179 | 6186 | }; |
|
| 6180 | 6187 | let ptrVal = try lowerExpr(self, args[0]); |
|
| 6181 | 6188 | let lenVal = try lowerExpr(self, args[1]); |
|
| 6182 | 6189 | let mut capVal = lenVal; |
lib/std/lang/parser.rad
+39 -21
| 3 | 3 | ||
| 4 | 4 | use std::mem; |
|
| 5 | 5 | use std::io; |
|
| 6 | 6 | use std::fmt; |
|
| 7 | 7 | use std::lang::alloc; |
|
| 8 | + | use std::lang::types; |
|
| 8 | 9 | use std::lang::ast; |
|
| 9 | 10 | use std::lang::strings; |
|
| 10 | 11 | use std::lang::scanner; |
|
| 11 | 12 | ||
| 12 | 13 | /// Maximum `u32` value. |
| 779 | 780 | )); |
|
| 780 | 781 | } |
|
| 781 | 782 | throw failParsing(p, "expected assignment after `set`"); |
|
| 782 | 783 | } |
|
| 783 | 784 | ||
| 784 | - | /// Parse leading attributes attached to the next declaration statement. |
|
| 785 | + | /// Parse leading attributes and declaration modifiers. |
|
| 785 | 786 | fn parseAttributes(p: *mut Parser) -> ?ast::Attributes { |
|
| 786 | 787 | let mut attrs = ast::nodeSlice(p.arena, 4); |
|
| 787 | 788 | ||
| 788 | 789 | if let attr = tryParseAnnotation(p) { |
|
| 789 | 790 | attrs.append(attr, p.allocator); |
|
| 790 | 791 | } |
|
| 791 | 792 | if consume(p, scanner::TokenKind::Export) { |
|
| 792 | - | let attrNode = nodeAttribute(p, ast::Attribute::Export); |
|
| 793 | - | attrs.append(attrNode, p.allocator); |
|
| 793 | + | attrs.append(nodeAttribute(p, ast::Attribute::Export), p.allocator); |
|
| 794 | + | } |
|
| 795 | + | if consume(p, scanner::TokenKind::Unsafe) { |
|
| 796 | + | attrs.append(nodeAttribute(p, ast::Attribute::Unsafe), p.allocator); |
|
| 794 | 797 | } |
|
| 795 | 798 | if attrs.len > 0 { |
|
| 796 | 799 | return ast::Attributes { list: attrs }; |
|
| 797 | 800 | } |
|
| 798 | 801 | return nil; |
| 831 | 834 | { |
|
| 832 | 835 | // TODO: Why is `parseStmt` checking for attributes? |
|
| 833 | 836 | // We should have a `parseDecl` which is top-level, and `parseStmt` which |
|
| 834 | 837 | // is inside functions. |
|
| 835 | 838 | let attrs = parseAttributes(p); |
|
| 836 | - | if attrs <> nil { |
|
| 839 | + | if let list = attrs { |
|
| 840 | + | if ast::attributesContains(&list, ast::Attribute::Unsafe) |
|
| 841 | + | and p.current.kind <> scanner::TokenKind::Fn |
|
| 842 | + | and p.current.kind <> scanner::TokenKind::Mod |
|
| 843 | + | { |
|
| 844 | + | throw failParsing(p, "`unsafe` is only allowed on functions and modules"); |
|
| 845 | + | } |
|
| 837 | 846 | let allowed: bool = |
|
| 838 | 847 | p.current.kind == scanner::TokenKind::Fn or |
|
| 839 | 848 | p.current.kind == scanner::TokenKind::Union or |
|
| 840 | 849 | p.current.kind == scanner::TokenKind::Record or |
|
| 841 | 850 | p.current.kind == scanner::TokenKind::Mod or |
| 1856 | 1865 | return node(p, ast::NodeValue::FnDecl( |
|
| 1857 | 1866 | ast::FnDecl { name, sig, body, attrs: fnAttrs } |
|
| 1858 | 1867 | )); |
|
| 1859 | 1868 | } |
|
| 1860 | 1869 | ||
| 1861 | - | /// Parse a pointer or slice type. |
|
| 1862 | - | fn parsePointerType(p: *mut Parser) -> *ast::Node |
|
| 1863 | - | throws (ParseError) |
|
| 1864 | - | { |
|
| 1865 | - | try expect(p, scanner::TokenKind::Star, "expected `*`"); |
|
| 1870 | + | /// Parse a pointer-like type after its ownership prefix. |
|
| 1871 | + | fn parsePointerLikeType( |
|
| 1872 | + | p: *mut Parser, |
|
| 1873 | + | class: types::PointerClass, |
|
| 1874 | + | ) -> *ast::Node throws (ParseError) { |
|
| 1866 | 1875 | let mutable = consume(p, scanner::TokenKind::Mut); |
|
| 1867 | 1876 | ||
| 1868 | 1877 | if consume(p, scanner::TokenKind::LBracket) { |
|
| 1869 | 1878 | let itemType = try parseType(p); |
|
| 1870 | 1879 | try expect(p, scanner::TokenKind::RBracket, "expected `]` after slice element type"); |
|
| 1871 | 1880 | ||
| 1872 | 1881 | return node(p, ast::NodeValue::TypeSig( |
|
| 1873 | - | ast::TypeSig::Slice { itemType, mutable } |
|
| 1882 | + | ast::TypeSig::Slice { class, itemType, mutable } |
|
| 1874 | 1883 | )); |
|
| 1875 | 1884 | } |
|
| 1876 | - | // Check for `*opaque Trait` or `*mut opaque Trait`. |
|
| 1885 | + | // Check for an opaque trait object. |
|
| 1877 | 1886 | if consume(p, scanner::TokenKind::Opaque) { |
|
| 1878 | 1887 | if check(p, scanner::TokenKind::Ident) or check(p, scanner::TokenKind::Super) { |
|
| 1879 | 1888 | let traitName = try parseTypePath(p); |
|
| 1880 | 1889 | return node(p, ast::NodeValue::TypeSig( |
|
| 1881 | - | ast::TypeSig::TraitObject { traitName, mutable } |
|
| 1890 | + | ast::TypeSig::TraitObject { class, traitName, mutable } |
|
| 1882 | 1891 | )); |
|
| 1883 | 1892 | } |
|
| 1884 | - | // Plain `*opaque`. |
|
| 1893 | + | // Plain opaque target. |
|
| 1894 | + | let valueType = node(p, ast::NodeValue::TypeSig(ast::TypeSig::Opaque)); |
|
| 1885 | 1895 | return node(p, ast::NodeValue::TypeSig( |
|
| 1886 | - | ast::TypeSig::Pointer { |
|
| 1887 | - | valueType: node(p, ast::NodeValue::TypeSig(ast::TypeSig::Opaque)), |
|
| 1888 | - | mutable, |
|
| 1889 | - | } |
|
| 1896 | + | ast::TypeSig::Pointer { class, valueType, mutable } |
|
| 1890 | 1897 | )); |
|
| 1891 | 1898 | } |
|
| 1892 | 1899 | let valueType = try parseType(p); |
|
| 1893 | 1900 | ||
| 1894 | 1901 | return node(p, ast::NodeValue::TypeSig( |
|
| 1895 | - | ast::TypeSig::Pointer { valueType, mutable } |
|
| 1902 | + | ast::TypeSig::Pointer { class, valueType, mutable } |
|
| 1896 | 1903 | )); |
|
| 1897 | 1904 | } |
|
| 1898 | 1905 | ||
| 1899 | 1906 | /// Parse an array type. |
|
| 1900 | 1907 | fn parseArrayType(p: *mut Parser) -> *ast::Node |
| 1945 | 1952 | return node(p, ast::NodeValue::TypeSig( |
|
| 1946 | 1953 | ast::TypeSig::Optional { valueType } |
|
| 1947 | 1954 | )); |
|
| 1948 | 1955 | } |
|
| 1949 | 1956 | case scanner::TokenKind::Star => { |
|
| 1950 | - | return try parsePointerType(p); |
|
| 1957 | + | advance(p); |
|
| 1958 | + | let class = types::PointerClass::Unsafe |
|
| 1959 | + | if consume(p, scanner::TokenKind::Unsafe) |
|
| 1960 | + | else types::PointerClass::Owned; |
|
| 1961 | + | return try parsePointerLikeType(p, class); |
|
| 1962 | + | } |
|
| 1963 | + | case scanner::TokenKind::Amp => { |
|
| 1964 | + | advance(p); |
|
| 1965 | + | return try parsePointerLikeType(p, types::PointerClass::Ref); |
|
| 1951 | 1966 | } |
|
| 1952 | 1967 | case scanner::TokenKind::LBracket => { |
|
| 1953 | 1968 | return try parseArrayType(p); |
|
| 1954 | 1969 | } |
|
| 1955 | 1970 | case scanner::TokenKind::Super, scanner::TokenKind::Ident => { |
| 2252 | 2267 | /// Parse a trait method signature. |
|
| 2253 | 2268 | /// Syntax: `fn (*Trait) fnord(<params>) -> ReturnType;` |
|
| 2254 | 2269 | fn parseTraitMethodSig(p: *mut Parser) -> *ast::Node |
|
| 2255 | 2270 | throws (ParseError) |
|
| 2256 | 2271 | { |
|
| 2272 | + | let attrs = parseAttributes(p); |
|
| 2257 | 2273 | try expect(p, scanner::TokenKind::Fn, "expected `fn`"); |
|
| 2258 | 2274 | try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver"); |
|
| 2259 | 2275 | ||
| 2260 | 2276 | let receiver = try parseType(p); |
|
| 2261 | 2277 |
| 2263 | 2279 | ||
| 2264 | 2280 | let name = try parseIdent(p, "expected method name"); |
|
| 2265 | 2281 | let sig = try parseFnTypeSig(p); |
|
| 2266 | 2282 | try expect(p, scanner::TokenKind::Semicolon, "expected `;` after method signature"); |
|
| 2267 | 2283 | ||
| 2268 | - | return node(p, ast::NodeValue::TraitMethodSig { name, receiver, sig }); |
|
| 2284 | + | return node(p, ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs }); |
|
| 2269 | 2285 | } |
|
| 2270 | 2286 | ||
| 2271 | 2287 | /// Parse an instance block. |
|
| 2272 | 2288 | /// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }` |
|
| 2273 | 2289 | /// |
| 2281 | 2297 | try expect(p, scanner::TokenKind::For, "expected `for` after trait name"); |
|
| 2282 | 2298 | let targetType = try parseTypePath(p); |
|
| 2283 | 2299 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type"); |
|
| 2284 | 2300 | ||
| 2285 | 2301 | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2302 | + | ||
| 2286 | 2303 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2287 | 2304 | not check(p, scanner::TokenKind::Eof) |
|
| 2288 | 2305 | { |
|
| 2306 | + | let attrs = parseAttributes(p); |
|
| 2289 | 2307 | try expect(p, scanner::TokenKind::Fn, "expected `fn`"); |
|
| 2290 | - | let method = try parseMethodDecl(p, nil); |
|
| 2308 | + | let method = try parseMethodDecl(p, attrs); |
|
| 2291 | 2309 | ||
| 2292 | 2310 | methods.append(method, p.allocator); |
|
| 2293 | 2311 | } |
|
| 2294 | 2312 | try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods"); |
|
| 2295 | 2313 |
lib/std/lang/parser/tests.rad
+153 -21
| 2 | 2 | ||
| 3 | 3 | use std::mem; |
|
| 4 | 4 | use std::fmt; |
|
| 5 | 5 | use std::testing; |
|
| 6 | 6 | use std::lang::ast; |
|
| 7 | + | use std::lang::types; |
|
| 8 | + | use std::lang::alloc; |
|
| 9 | + | use std::lang::sexpr; |
|
| 10 | + | use std::lang::ast::printer; |
|
| 7 | 11 | use std::lang::scanner; |
|
| 8 | 12 | use std::lang::strings; |
|
| 9 | 13 | ||
| 10 | 14 | /// Unified arena size. |
|
| 11 | 15 | constant ARENA_SIZE: u32 = 2097152; |
| 783 | 787 | else throw testing::TestError::Failed; |
|
| 784 | 788 | ||
| 785 | 789 | try expectIntType(opt, 4, ast::Signedness::Signed); |
|
| 786 | 790 | } |
|
| 787 | 791 | ||
| 788 | - | /// Test parsing a pointer type, including `mut`. |
|
| 789 | - | @test fn testParseTypePointer() throws (testing::TestError) { |
|
| 790 | - | let node = try! parseTypeStr("*mut i32"); |
|
| 792 | + | /// Parse an `i32` pointer type and verify its class and mutability. |
|
| 793 | + | fn expectI32Pointer( |
|
| 794 | + | source: *[u8], |
|
| 795 | + | class: types::PointerClass, |
|
| 796 | + | mutable: bool, |
|
| 797 | + | ) throws (testing::TestError) { |
|
| 798 | + | let node = try! parseTypeStr(source); |
|
| 791 | 799 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 792 | 800 | else throw testing::TestError::Failed; |
|
| 793 | - | let case ast::TypeSig::Pointer { valueType, mutable } = sig |
|
| 794 | - | else throw testing::TestError::Failed; |
|
| 801 | + | let case ast::TypeSig::Pointer { |
|
| 802 | + | class: actualClass, valueType, mutable: actualMutable, |
|
| 803 | + | } = sig else throw testing::TestError::Failed; |
|
| 795 | 804 | ||
| 805 | + | assert actualClass == class; |
|
| 796 | 806 | try expectIntType(valueType, 4, ast::Signedness::Signed); |
|
| 797 | - | try testing::expect(mutable); |
|
| 807 | + | assert actualMutable == mutable; |
|
| 808 | + | } |
|
| 809 | + | ||
| 810 | + | /// Test parsing a mutable owned pointer. |
|
| 811 | + | @test fn testParseTypePointer() throws (testing::TestError) { |
|
| 812 | + | try expectI32Pointer("*mut i32", types::PointerClass::Owned, true); |
|
| 798 | 813 | } |
|
| 799 | 814 | ||
| 800 | - | /// Test parsing an immutable pointer type. |
|
| 815 | + | /// Test parsing an immutable owned pointer. |
|
| 801 | 816 | @test fn testParseTypePointerImmutable() throws (testing::TestError) { |
|
| 802 | - | let node = try! parseTypeStr("*i32"); |
|
| 803 | - | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 804 | - | else throw testing::TestError::Failed; |
|
| 805 | - | let case ast::TypeSig::Pointer { valueType, mutable } = sig |
|
| 806 | - | else throw testing::TestError::Failed; |
|
| 817 | + | try expectI32Pointer("*i32", types::PointerClass::Owned, false); |
|
| 818 | + | } |
|
| 807 | 819 | ||
| 808 | - | try expectIntType(valueType, 4, ast::Signedness::Signed); |
|
| 809 | - | try testing::expect(not mutable); |
|
| 820 | + | /// Test parsing immutable and mutable references. |
|
| 821 | + | @test fn testParseTypeRef() throws (testing::TestError) { |
|
| 822 | + | try expectI32Pointer("&i32", types::PointerClass::Ref, false); |
|
| 823 | + | try expectI32Pointer("&mut i32", types::PointerClass::Ref, true); |
|
| 824 | + | } |
|
| 825 | + | ||
| 826 | + | /// Test parsing immutable and mutable unsafe pointers. |
|
| 827 | + | @test fn testParseTypeUnsafePointer() throws (testing::TestError) { |
|
| 828 | + | try expectI32Pointer("*unsafe i32", types::PointerClass::Unsafe, false); |
|
| 829 | + | try expectI32Pointer("*unsafe mut i32", types::PointerClass::Unsafe, true); |
|
| 810 | 830 | } |
|
| 811 | 831 | ||
| 812 | 832 | /// Test parsing a slice type. |
|
| 813 | 833 | @test fn testParseTypeSlice() throws (testing::TestError) { |
|
| 814 | 834 | let node = try! parseTypeStr("*[u8]"); |
|
| 815 | 835 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 816 | 836 | else throw testing::TestError::Failed; |
|
| 817 | - | let case ast::TypeSig::Slice { itemType, mutable } = sig |
|
| 837 | + | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 818 | 838 | else throw testing::TestError::Failed; |
|
| 819 | 839 | ||
| 840 | + | assert class == types::PointerClass::Owned; |
|
| 820 | 841 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 821 | - | try testing::expect(not mutable); |
|
| 842 | + | assert not mutable; |
|
| 822 | 843 | } |
|
| 823 | 844 | ||
| 824 | 845 | /// Test parsing a mutable slice type. |
|
| 825 | 846 | @test fn testParseTypeSliceMutable() throws (testing::TestError) { |
|
| 826 | 847 | let node = try! parseTypeStr("*mut [u8]"); |
|
| 827 | 848 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 828 | 849 | else throw testing::TestError::Failed; |
|
| 829 | - | let case ast::TypeSig::Slice { itemType, mutable } = sig |
|
| 850 | + | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 830 | 851 | else throw testing::TestError::Failed; |
|
| 831 | 852 | ||
| 853 | + | assert class == types::PointerClass::Owned; |
|
| 832 | 854 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 833 | - | try testing::expect(mutable); |
|
| 855 | + | assert mutable; |
|
| 856 | + | } |
|
| 857 | + | ||
| 858 | + | /// Test parsing reference and unsafe slice classes. |
|
| 859 | + | @test fn testParseTypeSliceClasses() throws (testing::TestError) { |
|
| 860 | + | let refNode = try! parseTypeStr("&[u8]"); |
|
| 861 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { |
|
| 862 | + | class: refClass, .. |
|
| 863 | + | }) = refNode.value else throw testing::TestError::Failed; |
|
| 864 | + | assert refClass == types::PointerClass::Ref; |
|
| 865 | + | ||
| 866 | + | let unsafeNode = try! parseTypeStr("*unsafe [u8]"); |
|
| 867 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { |
|
| 868 | + | class: unsafeClass, .. |
|
| 869 | + | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 870 | + | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 871 | + | } |
|
| 872 | + | ||
| 873 | + | /// Test parsing trait object pointer classes. |
|
| 874 | + | @test fn testParseTypeTraitObjectClasses() throws (testing::TestError) { |
|
| 875 | + | let ownedNode = try! parseTypeStr("*opaque Read"); |
|
| 876 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 877 | + | class: ownedClass, .. |
|
| 878 | + | }) = ownedNode.value else throw testing::TestError::Failed; |
|
| 879 | + | assert ownedClass == types::PointerClass::Owned; |
|
| 880 | + | ||
| 881 | + | let refNode = try! parseTypeStr("&opaque Read"); |
|
| 882 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 883 | + | class: refClass, .. |
|
| 884 | + | }) = refNode.value else throw testing::TestError::Failed; |
|
| 885 | + | assert refClass == types::PointerClass::Ref; |
|
| 886 | + | ||
| 887 | + | let unsafeNode = try! parseTypeStr("*unsafe opaque Read"); |
|
| 888 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 889 | + | class: unsafeClass, .. |
|
| 890 | + | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 891 | + | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 834 | 892 | } |
|
| 835 | 893 | ||
| 836 | 894 | /// Test parsing an array type. |
|
| 837 | 895 | @test fn testParseTypeArray() throws (testing::TestError) { |
|
| 838 | 896 | let node = try! parseTypeStr("[i32; 4]"); |
| 964 | 1022 | try expectIntType(param0, 4, ast::Signedness::Signed); |
|
| 965 | 1023 | ||
| 966 | 1024 | let param1 = sig.params[1]; |
|
| 967 | 1025 | let case ast::NodeValue::TypeSig(p1) = param1.value |
|
| 968 | 1026 | else throw testing::TestError::Failed; |
|
| 969 | - | let case ast::TypeSig::Pointer { valueType: ptrTarget, mutable: _ } = p1 |
|
| 1027 | + | let case ast::TypeSig::Pointer { valueType: ptrTarget, .. } = p1 |
|
| 970 | 1028 | else throw testing::TestError::Failed; |
|
| 971 | 1029 | try expectIntType(ptrTarget, 1, ast::Signedness::Unsigned); |
|
| 972 | 1030 | ||
| 973 | 1031 | try testing::expect(sig.returnType <> nil); |
|
| 974 | 1032 | } |
| 1071 | 1129 | let mut parser = super::mkParser(scanner::SourceLoc::String, "void", &mut arena, &mut STRING_POOL); |
|
| 1072 | 1130 | super::advance(&mut parser); |
|
| 1073 | 1131 | try testing::expect(super::check(&parser, scanner::TokenKind::Ident)); |
|
| 1074 | 1132 | } |
|
| 1075 | 1133 | ||
| 1134 | + | /// Test parsing the unsafe function modifier. |
|
| 1135 | + | @test fn testParseUnsafeFnDecl() throws (testing::TestError) { |
|
| 1136 | + | let node = try! parseStmtStr("unsafe fn run() {}"); |
|
| 1137 | + | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1138 | + | else throw testing::TestError::Failed; |
|
| 1139 | + | let attrs = decl.attrs |
|
| 1140 | + | else throw testing::TestError::Failed; |
|
| 1141 | + | ||
| 1142 | + | try testing::expect(attrs.list.len == 1); |
|
| 1143 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe)); |
|
| 1144 | + | } |
|
| 1145 | + | ||
| 1146 | + | /// Test rejecting `unsafe` on declarations where it has no semantics. |
|
| 1147 | + | @test fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) { |
|
| 1148 | + | let recordDecl: ?*ast::Node = try? parseStmtStr("unsafe record R {}"); |
|
| 1149 | + | try testing::expect(recordDecl == nil); |
|
| 1150 | + | let constDecl: ?*ast::Node = try? parseStmtStr("unsafe constant X = 1;"); |
|
| 1151 | + | try testing::expect(constDecl == nil); |
|
| 1152 | + | } |
|
| 1153 | + | ||
| 1154 | + | /// Test `unsafe` on the other declaration forms that support it. |
|
| 1155 | + | @test fn testParseUnsafeMethodAndModule() throws (testing::TestError) { |
|
| 1156 | + | let moduleNode = try! parseStmtStr("unsafe mod io;"); |
|
| 1157 | + | let case ast::NodeValue::Mod(moduleDecl) = moduleNode.value |
|
| 1158 | + | else throw testing::TestError::Failed; |
|
| 1159 | + | let moduleAttrs = moduleDecl.attrs else throw testing::TestError::Failed; |
|
| 1160 | + | assert ast::attributesContains(&moduleAttrs, ast::Attribute::Unsafe); |
|
| 1161 | + | ||
| 1162 | + | let instanceNode = try! parseStmtStr( |
|
| 1163 | + | "instance Read for Value { unsafe fn (value: &Value) get() {} }" |
|
| 1164 | + | ); |
|
| 1165 | + | let case ast::NodeValue::InstanceDecl { methods, .. } = instanceNode.value |
|
| 1166 | + | else throw testing::TestError::Failed; |
|
| 1167 | + | try testing::expect(methods.len == 1); |
|
| 1168 | + | let case ast::NodeValue::MethodDecl { attrs, .. } = methods[0].value |
|
| 1169 | + | else throw testing::TestError::Failed; |
|
| 1170 | + | let methodAttrs = attrs else throw testing::TestError::Failed; |
|
| 1171 | + | assert ast::attributesContains(&methodAttrs, ast::Attribute::Unsafe); |
|
| 1172 | + | ||
| 1173 | + | let mut printStorage: [u8; 4096] = undefined; |
|
| 1174 | + | let mut printArena = alloc::new(&mut printStorage[..]); |
|
| 1175 | + | let methodExpr = printer::toExpr(&mut printArena, methods[0]); |
|
| 1176 | + | let case sexpr::Expr::Block { items: methodItems, .. } = methodExpr |
|
| 1177 | + | else throw testing::TestError::Failed; |
|
| 1178 | + | let case sexpr::Expr::List { |
|
| 1179 | + | head: methodAttrsHead, tail: printedMethodAttrs, .. |
|
| 1180 | + | } = methodItems[0] else throw testing::TestError::Failed; |
|
| 1181 | + | assert mem::eq(methodAttrsHead, "attrs"); |
|
| 1182 | + | let case sexpr::Expr::Sym(methodAttr) = printedMethodAttrs[0] |
|
| 1183 | + | else throw testing::TestError::Failed; |
|
| 1184 | + | assert mem::eq(methodAttr, "@unsafe"); |
|
| 1185 | + | ||
| 1186 | + | let traitNode = try! parseStmtStr( |
|
| 1187 | + | "trait Read { unsafe fn (&Read) get(); }" |
|
| 1188 | + | ); |
|
| 1189 | + | let case ast::NodeValue::TraitDecl { methods: traitMethods, .. } = traitNode.value |
|
| 1190 | + | else throw testing::TestError::Failed; |
|
| 1191 | + | let case ast::NodeValue::TraitMethodSig { |
|
| 1192 | + | attrs: traitAttrs, .. |
|
| 1193 | + | } = traitMethods[0].value else throw testing::TestError::Failed; |
|
| 1194 | + | let traitMethodAttrs = traitAttrs else throw testing::TestError::Failed; |
|
| 1195 | + | assert ast::attributesContains(&traitMethodAttrs, ast::Attribute::Unsafe); |
|
| 1196 | + | ||
| 1197 | + | let traitMethodExpr = printer::toExpr(&mut printArena, traitMethods[0]); |
|
| 1198 | + | let case sexpr::Expr::List { tail: traitMethodItems, .. } = traitMethodExpr |
|
| 1199 | + | else throw testing::TestError::Failed; |
|
| 1200 | + | let case sexpr::Expr::List { |
|
| 1201 | + | head: traitAttrsHead, tail: printedTraitAttrs, .. |
|
| 1202 | + | } = traitMethodItems[0] else throw testing::TestError::Failed; |
|
| 1203 | + | assert mem::eq(traitAttrsHead, "attrs"); |
|
| 1204 | + | let case sexpr::Expr::Sym(traitAttr) = printedTraitAttrs[0] |
|
| 1205 | + | else throw testing::TestError::Failed; |
|
| 1206 | + | assert mem::eq(traitAttr, "@unsafe"); |
|
| 1207 | + | } |
|
| 1208 | + | ||
| 1076 | 1209 | /// Test parsing a function declaration with attributes. |
|
| 1077 | 1210 | @test fn testParseFnDeclAttributes() throws (testing::TestError) { |
|
| 1078 | 1211 | let node = try! parseStmtStr("export fn run();"); |
|
| 1079 | 1212 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1080 | 1213 | else throw testing::TestError::Failed; |
| 1164 | 1297 | @test fn testParseTypeBool() throws (testing::TestError) { |
|
| 1165 | 1298 | let node = try! parseTypeStr("bool"); |
|
| 1166 | 1299 | try expectType(node, ast::TypeSig::Bool); |
|
| 1167 | 1300 | } |
|
| 1168 | 1301 | ||
| 1169 | - | ||
| 1170 | 1302 | /// Test parsing an unsigned integer type. |
|
| 1171 | 1303 | @test fn testParseTypeUnsigned() throws (testing::TestError) { |
|
| 1172 | 1304 | let node = try! parseTypeStr("u8"); |
|
| 1173 | 1305 | try expectType(node, ast::TypeSig::Integer { |
|
| 1174 | 1306 | width: 1, |
| 2081 | 2213 | try expectIdent(access.child, "field"); |
|
| 2082 | 2214 | } |
|
| 2083 | 2215 | } |
|
| 2084 | 2216 | ||
| 2085 | 2217 | /// Test parsing reference (address-of) expressions. |
|
| 2086 | - | @test fn testParseReferences() throws (testing::TestError) { |
|
| 2218 | + | @test fn testParseRefs() throws (testing::TestError) { |
|
| 2087 | 2219 | { |
|
| 2088 | 2220 | let refExpr = try! parseExprStr("&foo"); |
|
| 2089 | 2221 | let case ast::NodeValue::AddressOf(refNode) = refExpr.value |
|
| 2090 | 2222 | else throw testing::TestError::Failed; |
|
| 2091 | 2223 | try testing::expect(refNode.mutable == false); |
lib/std/lang/resolver.rad
+76 -38
| 14 | 14 | // TODO: Have different types for positional vs. named field records. |
|
| 15 | 15 | ||
| 16 | 16 | use std::mem; |
|
| 17 | 17 | use std::io; |
|
| 18 | 18 | use std::lang::alloc; |
|
| 19 | + | use std::lang::types; |
|
| 19 | 20 | use std::lang::ast; |
|
| 20 | 21 | use std::lang::parser; |
|
| 21 | 22 | use std::lang::module; |
|
| 22 | 23 | ||
| 23 | 24 | /// Maximum number of diagnostics recorded. |
| 276 | 277 | /// Range types, eg. `start..end`. |
|
| 277 | 278 | Range { |
|
| 278 | 279 | start: ?*Type, |
|
| 279 | 280 | end: ?*Type, |
|
| 280 | 281 | }, |
|
| 281 | - | /// Eg. `*T`. |
|
| 282 | + | /// Pointer-like address. |
|
| 282 | 283 | Pointer { |
|
| 284 | + | class: types::PointerClass, |
|
| 283 | 285 | target: *Type, |
|
| 284 | 286 | mutable: bool, |
|
| 285 | 287 | }, |
|
| 286 | - | /// Eg. `*[i32]`. |
|
| 288 | + | /// Pointer-like slice. |
|
| 287 | 289 | Slice { |
|
| 290 | + | class: types::PointerClass, |
|
| 288 | 291 | item: *Type, |
|
| 289 | 292 | mutable: bool, |
|
| 290 | 293 | }, |
|
| 291 | 294 | /// Eg. `[i32; 32]`. |
|
| 292 | 295 | Array(ArrayType), |
| 294 | 297 | Optional(*Type), |
|
| 295 | 298 | /// Eg. `fn id(i32) -> i32`. |
|
| 296 | 299 | Fn(*FnType), |
|
| 297 | 300 | /// Named, ie. user-defined types, includes union variants. |
|
| 298 | 301 | Nominal(*NominalType), |
|
| 299 | - | /// Trait object. An erased type with v-table. |
|
| 302 | + | /// An erased pointer-like type with a v-table. |
|
| 300 | 303 | TraitObject { |
|
| 304 | + | /// Ownership and safety class. |
|
| 305 | + | class: types::PointerClass, |
|
| 301 | 306 | /// Trait definition. |
|
| 302 | 307 | traitInfo: *TraitType, |
|
| 303 | 308 | /// Whether the pointer is mutable. |
|
| 304 | 309 | mutable: bool, |
|
| 305 | 310 | }, |
| 725 | 730 | by: MatchBy, |
|
| 726 | 731 | } |
|
| 727 | 732 | ||
| 728 | 733 | /// Unwrap a pointer type for pattern matching. |
|
| 729 | 734 | export fn unwrapMatchSubject(ty: Type) -> MatchSubject { |
|
| 730 | - | if let case Type::Pointer { target, mutable } = ty { |
|
| 735 | + | if let case Type::Pointer { target, mutable, .. } = ty { |
|
| 731 | 736 | let by = MatchBy::MutRef if mutable else MatchBy::Ref; |
|
| 732 | 737 | return MatchSubject { effectiveTy: *target, by }; |
|
| 733 | 738 | } |
|
| 734 | 739 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 735 | 740 | } |
| 1707 | 1712 | } |
|
| 1708 | 1713 | return nil; |
|
| 1709 | 1714 | } |
|
| 1710 | 1715 | } |
|
| 1711 | 1716 | } |
|
| 1712 | - | case Type::Pointer { target: lhsTarget, mutable: lhsMutable } => { |
|
| 1713 | - | let case Type::Pointer { target: rhsTarget, mutable: rhsMutable } = from |
|
| 1717 | + | case Type::Pointer { target: lhsTarget, mutable: lhsMutable, .. } => { |
|
| 1718 | + | let case Type::Pointer { target: rhsTarget, mutable: rhsMutable, .. } = from |
|
| 1714 | 1719 | else return nil; |
|
| 1715 | 1720 | ||
| 1716 | 1721 | // Allow coercion from `*T` to `*opaque`, and `*mut T` to `*mut opaque`. |
|
| 1717 | 1722 | if *lhsTarget == Type::Opaque { |
|
| 1718 | 1723 | if lhsMutable and not rhsMutable { |
| 1735 | 1740 | if let case Type::Optional(fromInner) = from { |
|
| 1736 | 1741 | return isAssignable(self, *inner, *fromInner, rval); |
|
| 1737 | 1742 | } |
|
| 1738 | 1743 | return nil; |
|
| 1739 | 1744 | } |
|
| 1740 | - | case Type::TraitObject { traitInfo, mutable: lhsMutable } => { |
|
| 1745 | + | case Type::TraitObject { |
|
| 1746 | + | class: lhsClass, traitInfo, mutable: lhsMutable, |
|
| 1747 | + | } => { |
|
| 1741 | 1748 | // Coerce `*T` or `*mut T` where `T` implements the trait. |
|
| 1742 | - | if let case Type::Pointer { target, mutable: rhsMutable } = from { |
|
| 1749 | + | if let case Type::Pointer { target, mutable: rhsMutable, .. } = from { |
|
| 1743 | 1750 | if lhsMutable and not rhsMutable { |
|
| 1744 | 1751 | return nil; |
|
| 1745 | 1752 | } |
|
| 1746 | 1753 | // Look up instance registry. |
|
| 1747 | 1754 | if let inst = findInstance(self, traitInfo, *target) { |
|
| 1748 | 1755 | return Coercion::TraitObject { traitInfo, inst }; |
|
| 1749 | 1756 | } |
|
| 1750 | 1757 | } |
|
| 1751 | 1758 | // Identity: same trait object. |
|
| 1752 | - | if let case Type::TraitObject { traitInfo: rhsTrait, mutable: rhsMutable } = from { |
|
| 1753 | - | if traitInfo <> rhsTrait { |
|
| 1759 | + | if let case Type::TraitObject { |
|
| 1760 | + | class: rhsClass, traitInfo: rhsTrait, mutable: rhsMutable, |
|
| 1761 | + | } = from { |
|
| 1762 | + | if lhsClass <> rhsClass or traitInfo <> rhsTrait { |
|
| 1754 | 1763 | return nil; |
|
| 1755 | 1764 | } |
|
| 1756 | 1765 | if lhsMutable and not rhsMutable { |
|
| 1757 | 1766 | return nil; |
|
| 1758 | 1767 | } |
|
| 1759 | 1768 | return Coercion::Identity; |
|
| 1760 | 1769 | } |
|
| 1761 | 1770 | return nil; |
|
| 1762 | 1771 | } |
|
| 1763 | - | case Type::Slice { item: lhsItem, mutable: lhsMutable } => { |
|
| 1772 | + | case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } => { |
|
| 1764 | 1773 | match from { |
|
| 1765 | - | case Type::Slice { item: rhsItem, mutable: rhsMutable } => { |
|
| 1774 | + | case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } => { |
|
| 1775 | + | if lhsClass <> rhsClass { |
|
| 1776 | + | return nil; |
|
| 1777 | + | } |
|
| 1766 | 1778 | if lhsMutable and not rhsMutable { |
|
| 1767 | 1779 | return nil; |
|
| 1768 | 1780 | } |
|
| 1769 | 1781 | // Allow coercion from `*[T]` to `*[opaque]`, and `*mut [T]` to `*mut [opaque]`. |
|
| 1770 | 1782 | if *lhsItem == Type::Opaque { |
| 1845 | 1857 | export fn typesEqual(a: Type, b: Type) -> bool { |
|
| 1846 | 1858 | if a == b { |
|
| 1847 | 1859 | return true; |
|
| 1848 | 1860 | } |
|
| 1849 | 1861 | match a { |
|
| 1850 | - | case Type::Pointer { target: aTarget, mutable: aMutable } => { |
|
| 1851 | - | let case Type::Pointer { target: bTarget, mutable: bMutable } = b else return false; |
|
| 1852 | - | return aMutable == bMutable and typesEqual(*aTarget, *bTarget); |
|
| 1862 | + | case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } => { |
|
| 1863 | + | let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b |
|
| 1864 | + | else return false; |
|
| 1865 | + | return aClass == bClass and aMutable == bMutable and typesEqual(*aTarget, *bTarget); |
|
| 1853 | 1866 | } |
|
| 1854 | - | case Type::Slice { item: aItem, mutable: aMutable } => { |
|
| 1855 | - | let case Type::Slice { item: bItem, mutable: bMutable } = b else return false; |
|
| 1856 | - | return aMutable == bMutable and typesEqual(*aItem, *bItem); |
|
| 1867 | + | case Type::Slice { class: aClass, item: aItem, mutable: aMutable } => { |
|
| 1868 | + | let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b |
|
| 1869 | + | else return false; |
|
| 1870 | + | return aClass == bClass and aMutable == bMutable and typesEqual(*aItem, *bItem); |
|
| 1857 | 1871 | } |
|
| 1858 | 1872 | case Type::Array(aa) => { |
|
| 1859 | 1873 | let case Type::Array(ab) = b else return false; |
|
| 1860 | 1874 | return aa.length == ab.length and typesEqual(*aa.item, *ab.item); |
|
| 1861 | 1875 | } |
| 1892 | 1906 | if index >= recInfo.fields.len { |
|
| 1893 | 1907 | return nil; |
|
| 1894 | 1908 | } |
|
| 1895 | 1909 | return recInfo.fields[index]; |
|
| 1896 | 1910 | } |
|
| 1897 | - | case Type::Slice { item, mutable } => { |
|
| 1911 | + | case Type::Slice { item, mutable, .. } => { |
|
| 1898 | 1912 | match index { |
|
| 1899 | 1913 | case 0 => return RecordField { |
|
| 1900 | 1914 | name: PTR_FIELD, |
|
| 1901 | - | fieldType: Type::Pointer { target: item, mutable }, |
|
| 1915 | + | fieldType: Type::Pointer { |
|
| 1916 | + | class: types::PointerClass::Owned, target: item, mutable, |
|
| 1917 | + | }, |
|
| 1902 | 1918 | offset: 0, |
|
| 1903 | 1919 | }, |
|
| 1904 | 1920 | case 1 => return RecordField { |
|
| 1905 | 1921 | name: LEN_FIELD, |
|
| 1906 | 1922 | fieldType: Type::U32, |
| 2618 | 2634 | case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint), |
|
| 2619 | 2635 | case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value), |
|
| 2620 | 2636 | case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr), |
|
| 2621 | 2637 | case ast::NodeValue::Panic { message } => { |
|
| 2622 | 2638 | try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type. |
|
| 2639 | + | class: types::PointerClass::Owned, |
|
| 2623 | 2640 | item: allocType(self, Type::U8), |
|
| 2624 | 2641 | mutable: false |
|
| 2625 | 2642 | }); |
|
| 2626 | 2643 | return setNodeType(self, node, Type::Never); |
|
| 2627 | 2644 | }, |
|
| 2628 | 2645 | case ast::NodeValue::Assert { condition, message } => { |
|
| 2629 | 2646 | try visit(self, condition, Type::Bool); |
|
| 2630 | 2647 | try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type. |
|
| 2648 | + | class: types::PointerClass::Owned, |
|
| 2631 | 2649 | item: allocType(self, Type::U8), |
|
| 2632 | 2650 | mutable: false |
|
| 2633 | 2651 | }); |
|
| 2634 | 2652 | return setNodeType(self, node, Type::Void); |
|
| 2635 | 2653 | }, |
| 2664 | 2682 | } |
|
| 2665 | 2683 | case ast::NodeValue::String(text) => { |
|
| 2666 | 2684 | setNodeConstValue(self, node, ConstValue::String(text)); |
|
| 2667 | 2685 | let byteTy = allocType(self, Type::U8); |
|
| 2668 | 2686 | let sliceTy = allocType(self, Type::Slice { |
|
| 2687 | + | class: types::PointerClass::Owned, |
|
| 2669 | 2688 | item: byteTy, |
|
| 2670 | 2689 | mutable: false, |
|
| 2671 | 2690 | }); |
|
| 2672 | 2691 | return setNodeType(self, node, *sliceTy); |
|
| 2673 | 2692 | }, |
| 3330 | 3349 | actual: traitType.methods.len as u32 + methods.len as u32, |
|
| 3331 | 3350 | })); |
|
| 3332 | 3351 | } |
|
| 3333 | 3352 | ||
| 3334 | 3353 | for methodNode in methods { |
|
| 3335 | - | let case ast::NodeValue::TraitMethodSig { name, receiver, sig } = methodNode.value |
|
| 3354 | + | let case ast::NodeValue::TraitMethodSig { name, receiver, sig, .. } = methodNode.value |
|
| 3336 | 3355 | else continue; |
|
| 3337 | 3356 | let methodName = try nodeName(self, name); |
|
| 3338 | 3357 | ||
| 3339 | 3358 | // Reject duplicate method names. |
|
| 3340 | 3359 | if let _ = findTraitMethod(traitType, methodName) { |
| 3342 | 3361 | } |
|
| 3343 | 3362 | // Determine receiver mutability from the receiver type node |
|
| 3344 | 3363 | // and validate that the receiver points to the declaring trait. |
|
| 3345 | 3364 | let case ast::NodeValue::TypeSig(typeSig) = receiver.value |
|
| 3346 | 3365 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3347 | - | let case ast::TypeSig::Pointer { mutable, valueType } = typeSig |
|
| 3366 | + | let case ast::TypeSig::Pointer { mutable, valueType, .. } = typeSig |
|
| 3348 | 3367 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3349 | 3368 | let case ast::NodeValue::TypeSig(innerSig) = valueType.value |
|
| 3350 | 3369 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3351 | 3370 | let case ast::TypeSig::Nominal(nameNode) = innerSig |
|
| 3352 | 3371 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
| 3484 | 3503 | ||
| 3485 | 3504 | // Determine receiver mutability and validate receiver type. |
|
| 3486 | 3505 | // The receiver must be `*Type` or `*mut Type`. |
|
| 3487 | 3506 | let case ast::NodeValue::TypeSig(typeSig) = receiverType.value |
|
| 3488 | 3507 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 3489 | - | let case ast::TypeSig::Pointer { mutable: receiverMut, valueType } = typeSig |
|
| 3508 | + | let case ast::TypeSig::Pointer { mutable: receiverMut, valueType, .. } = typeSig |
|
| 3490 | 3509 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 3491 | 3510 | ||
| 3492 | 3511 | // Validate that the receiver type annotation matches the |
|
| 3493 | 3512 | // concrete type from the instance declaration. |
|
| 3494 | 3513 | let annotatedTy = try infer(self, valueType); |
| 3508 | 3527 | } |
|
| 3509 | 3528 | ||
| 3510 | 3529 | // Build the function type for the instance method. |
|
| 3511 | 3530 | // The receiver becomes the first parameter. |
|
| 3512 | 3531 | let receiverPtrType = Type::Pointer { |
|
| 3532 | + | class: types::PointerClass::Owned, |
|
| 3513 | 3533 | target: allocType(self, concreteType), |
|
| 3514 | 3534 | mutable: receiverMut, |
|
| 3515 | 3535 | }; |
|
| 3516 | 3536 | ||
| 3517 | 3537 | // Validate that the instance method's signature matches the |
| 3705 | 3725 | attrs: ?ast::Attributes, |
|
| 3706 | 3726 | ) throws (ResolveError) { |
|
| 3707 | 3727 | // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a |
|
| 3708 | 3728 | // nominal type. |
|
| 3709 | 3729 | let fullReceiverTy = try infer(self, receiverType); |
|
| 3710 | - | let case Type::Pointer { target, mutable: receiverMut } = fullReceiverTy |
|
| 3730 | + | let case Type::Pointer { target, mutable: receiverMut, .. } = fullReceiverTy |
|
| 3711 | 3731 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 3712 | 3732 | let concreteType = *target; |
|
| 3713 | 3733 | let case Type::Nominal(nominalTy) = concreteType |
|
| 3714 | 3734 | else throw emitError(self, receiverType, ErrorKind::ExpectedRecord); |
|
| 3715 | 3735 | try ensureNominalResolved(self, nominalTy, receiverType); |
| 3727 | 3747 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3728 | 3748 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3729 | 3749 | ||
| 3730 | 3750 | // Receiver is the first parameter. |
|
| 3731 | 3751 | let receiverPtrType = Type::Pointer { |
|
| 3752 | + | class: types::PointerClass::Owned, |
|
| 3732 | 3753 | target: allocType(self, concreteType), |
|
| 3733 | 3754 | mutable: receiverMut, |
|
| 3734 | 3755 | }; |
|
| 3735 | 3756 | paramTypes.append(allocType(self, receiverPtrType), a); |
|
| 3736 | 3757 |
| 4754 | 4775 | throws (ResolveError) |
|
| 4755 | 4776 | { |
|
| 4756 | 4777 | let mut bindTy = ty; |
|
| 4757 | 4778 | match matchBy { |
|
| 4758 | 4779 | case MatchBy::Value => {} |
|
| 4759 | - | case MatchBy::Ref => set bindTy = Type::Pointer { target: allocType(self, ty), mutable: false }, |
|
| 4760 | - | case MatchBy::MutRef => set bindTy = Type::Pointer { target: allocType(self, ty), mutable: true }, |
|
| 4780 | + | case MatchBy::Ref => set bindTy = Type::Pointer { |
|
| 4781 | + | class: types::PointerClass::Owned, target: allocType(self, ty), mutable: false, |
|
| 4782 | + | }, |
|
| 4783 | + | case MatchBy::MutRef => set bindTy = Type::Pointer { |
|
| 4784 | + | class: types::PointerClass::Owned, target: allocType(self, ty), mutable: true, |
|
| 4785 | + | }, |
|
| 4761 | 4786 | } |
|
| 4762 | 4787 | match binding.value { |
|
| 4763 | 4788 | case ast::NodeValue::Placeholder => { |
|
| 4764 | 4789 | // Nothing to do. |
|
| 4765 | 4790 | } |
| 4925 | 4950 | expected: 2, |
|
| 4926 | 4951 | actual: args.len as u32, |
|
| 4927 | 4952 | })); |
|
| 4928 | 4953 | } |
|
| 4929 | 4954 | let ptrType = try visit(self, args[0], Type::Unknown); |
|
| 4930 | - | let case Type::Pointer { target, mutable } = ptrType else { |
|
| 4955 | + | let case Type::Pointer { target, mutable, .. } = ptrType else { |
|
| 4931 | 4956 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
|
| 4932 | 4957 | }; |
|
| 4933 | 4958 | let _ = try checkAssignable(self, args[1], Type::U32); |
|
| 4934 | 4959 | if args.len == 3 { |
|
| 4935 | 4960 | let _ = try checkAssignable(self, args[2], Type::U32); |
|
| 4936 | 4961 | } |
|
| 4937 | - | return setNodeType(self, node, Type::Slice { item: target, mutable }); |
|
| 4962 | + | return setNodeType(self, node, Type::Slice { |
|
| 4963 | + | class: types::PointerClass::Owned, item: target, mutable, |
|
| 4964 | + | }); |
|
| 4938 | 4965 | } |
|
| 4939 | 4966 | if args.len <> 1 { |
|
| 4940 | 4967 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 4941 | 4968 | expected: 1, |
|
| 4942 | 4969 | actual: args.len as u32, |
| 5003 | 5030 | // Intercept method calls on slices before inferring the callee. |
|
| 5004 | 5031 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
|
| 5005 | 5032 | let parentTy = try infer(self, access.parent); |
|
| 5006 | 5033 | let subjectTy = autoDeref(parentTy); |
|
| 5007 | 5034 | ||
| 5008 | - | if let case Type::Slice { item, mutable } = subjectTy { |
|
| 5035 | + | if let case Type::Slice { item, mutable, .. } = subjectTy { |
|
| 5009 | 5036 | let methodName = try nodeName(self, access.child); |
|
| 5010 | 5037 | if methodName == "append" { |
|
| 5011 | 5038 | return try resolveSliceAppend(self, node, access.parent, parentTy, call.args, item, mutable); |
|
| 5012 | 5039 | } |
|
| 5013 | 5040 | if methodName == "delete" { |
| 5046 | 5073 | if let t = typeFor(self, access.parent) { |
|
| 5047 | 5074 | set parentTy = t; |
|
| 5048 | 5075 | } |
|
| 5049 | 5076 | let subjectTy = autoDeref(parentTy); |
|
| 5050 | 5077 | ||
| 5051 | - | if let case Type::TraitObject { traitInfo, mutable: objMutable } = subjectTy { |
|
| 5078 | + | if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy { |
|
| 5052 | 5079 | let methodName = try nodeName(self, access.child); |
|
| 5053 | 5080 | let method = findTraitMethod(traitInfo, methodName) |
|
| 5054 | 5081 | else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName)); |
|
| 5055 | 5082 | ||
| 5056 | 5083 | // Reject mutable-receiver methods called on immutable trait objects. |
| 5177 | 5204 | case Type::Array(a) => { |
|
| 5178 | 5205 | try validateArraySliceBounds(self, range, a.length, node); |
|
| 5179 | 5206 | set item = a.item; |
|
| 5180 | 5207 | set capacity = a.length; |
|
| 5181 | 5208 | } |
|
| 5182 | - | case Type::Slice { item: i, mutable } => { |
|
| 5209 | + | case Type::Slice { item: i, mutable, .. } => { |
|
| 5183 | 5210 | if not mutable { throw emitError(self, container, ErrorKind::ImmutableBinding); } |
|
| 5184 | 5211 | set item = i; |
|
| 5185 | 5212 | } |
|
| 5186 | 5213 | else => throw emitError(self, container, ErrorKind::ExpectedIndexable), |
|
| 5187 | 5214 | } |
| 5682 | 5709 | ||
| 5683 | 5710 | return setNodeType(self, node, Type::U32); |
|
| 5684 | 5711 | } |
|
| 5685 | 5712 | throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName)); |
|
| 5686 | 5713 | } |
|
| 5687 | - | case Type::Slice { item, mutable } => { |
|
| 5714 | + | case Type::Slice { item, mutable, .. } => { |
|
| 5688 | 5715 | let fieldNode = access.child; |
|
| 5689 | 5716 | let fieldName = try nodeName(self, fieldNode); |
|
| 5690 | 5717 | ||
| 5691 | 5718 | if mem::eq(fieldName, PTR_FIELD) { |
|
| 5692 | 5719 | setRecordFieldIndex(self, fieldNode, 0); |
|
| 5693 | 5720 | let ptrTy = Type::Pointer { |
|
| 5721 | + | class: types::PointerClass::Owned, |
|
| 5694 | 5722 | target: item, |
|
| 5695 | 5723 | mutable, |
|
| 5696 | 5724 | }; |
|
| 5697 | 5725 | return setNodeType(self, node, ptrTy); |
|
| 5698 | 5726 | } |
| 5842 | 5870 | case Type::Array(arrayInfo) => { |
|
| 5843 | 5871 | try validateArraySliceBounds(self, range, arrayInfo.length, node); |
|
| 5844 | 5872 | set item = arrayInfo.item; |
|
| 5845 | 5873 | set capacity = arrayInfo.length; |
|
| 5846 | 5874 | } |
|
| 5847 | - | case Type::Slice { item: sliceItem, mutable } => { |
|
| 5875 | + | case Type::Slice { item: sliceItem, mutable, .. } => { |
|
| 5848 | 5876 | if addr.mutable and not mutable { |
|
| 5849 | 5877 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 5850 | 5878 | } |
|
| 5851 | 5879 | set item = sliceItem; |
|
| 5852 | 5880 | } |
|
| 5853 | 5881 | else => { |
|
| 5854 | 5882 | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
|
| 5855 | 5883 | } |
|
| 5856 | 5884 | } |
|
| 5857 | - | let sliceTy = Type::Slice { item, mutable: addr.mutable }; |
|
| 5885 | + | let sliceTy = Type::Slice { |
|
| 5886 | + | class: types::PointerClass::Owned, item, mutable: addr.mutable, |
|
| 5887 | + | }; |
|
| 5858 | 5888 | let alloc = allocType(self, sliceTy); |
|
| 5859 | 5889 | setSliceRangeInfo(self, node, SliceRangeInfo { |
|
| 5860 | 5890 | itemType: item, |
|
| 5861 | 5891 | mutable: addr.mutable, |
|
| 5862 | 5892 | capacity, |
| 5889 | 5919 | match addr.target.value { |
|
| 5890 | 5920 | case ast::NodeValue::ArrayLit(_), |
|
| 5891 | 5921 | ast::NodeValue::ArrayRepeatLit(_) => |
|
| 5892 | 5922 | { |
|
| 5893 | 5923 | let sliceTy = Type::Slice { |
|
| 5924 | + | class: types::PointerClass::Owned, |
|
| 5894 | 5925 | item: arrayInfo.item, |
|
| 5895 | 5926 | mutable: addr.mutable, |
|
| 5896 | 5927 | }; |
|
| 5897 | 5928 | return setNodeType(self, node, *allocType(self, sliceTy)); |
|
| 5898 | 5929 | } |
|
| 5899 | 5930 | else => {} |
|
| 5900 | 5931 | } |
|
| 5901 | 5932 | } |
|
| 5902 | 5933 | let pointerTy = Type::Pointer { |
|
| 5934 | + | class: types::PointerClass::Owned, |
|
| 5903 | 5935 | target: allocType(self, targetTy), |
|
| 5904 | 5936 | mutable: addr.mutable, |
|
| 5905 | 5937 | }; |
|
| 5906 | 5938 | return setNodeType(self, node, pointerTy); |
|
| 5907 | 5939 | } |
| 6610 | 6642 | let item = try infer(self, itemType); |
|
| 6611 | 6643 | let length = try checkSizeInt(self, length); |
|
| 6612 | 6644 | ||
| 6613 | 6645 | return Type::Array(ArrayType { item: allocType(self, item), length }); |
|
| 6614 | 6646 | } |
|
| 6615 | - | case ast::TypeSig::Slice { itemType, mutable } => { |
|
| 6647 | + | case ast::TypeSig::Slice { class, itemType, mutable } => { |
|
| 6616 | 6648 | let item = try infer(self, itemType); |
|
| 6617 | 6649 | return Type::Slice { |
|
| 6650 | + | class, |
|
| 6618 | 6651 | item: allocType(self, item), |
|
| 6619 | 6652 | mutable, |
|
| 6620 | 6653 | }; |
|
| 6621 | 6654 | } |
|
| 6622 | - | case ast::TypeSig::Pointer { valueType, mutable } => { |
|
| 6655 | + | case ast::TypeSig::Pointer { class, valueType, mutable } => { |
|
| 6623 | 6656 | let target = try infer(self, valueType); |
|
| 6624 | 6657 | return Type::Pointer { |
|
| 6658 | + | class, |
|
| 6625 | 6659 | target: allocType(self, target), |
|
| 6626 | 6660 | mutable, |
|
| 6627 | 6661 | }; |
|
| 6628 | 6662 | } |
|
| 6629 | 6663 | case ast::TypeSig::Optional { valueType } => { |
| 6675 | 6709 | throwList: &throwList[..], |
|
| 6676 | 6710 | localCount: 0, |
|
| 6677 | 6711 | }; |
|
| 6678 | 6712 | return Type::Fn(allocFnType(self, fnType)); |
|
| 6679 | 6713 | } |
|
| 6680 | - | case ast::TypeSig::TraitObject { traitName, mutable } => { |
|
| 6714 | + | case ast::TypeSig::TraitObject { class, traitName, mutable } => { |
|
| 6681 | 6715 | let sym = try resolveNamePath(self, traitName); |
|
| 6682 | 6716 | let case SymbolData::Trait(traitInfo) = sym.data |
|
| 6683 | 6717 | else throw emitError(self, traitName, ErrorKind::Internal); |
|
| 6684 | 6718 | setNodeSymbol(self, traitName, sym); |
|
| 6685 | 6719 | ||
| 6686 | - | return Type::TraitObject { traitInfo, mutable }; |
|
| 6720 | + | return Type::TraitObject { |
|
| 6721 | + | class, |
|
| 6722 | + | traitInfo, |
|
| 6723 | + | mutable, |
|
| 6724 | + | }; |
|
| 6687 | 6725 | } |
|
| 6688 | 6726 | } |
|
| 6689 | 6727 | } |
|
| 6690 | 6728 | ||
| 6691 | 6729 | /// Check if a type can be used for inferrence. |
lib/std/lang/resolver/printer.rad
+3 -3
| 97 | 97 | io::print("i32"); |
|
| 98 | 98 | } |
|
| 99 | 99 | case super::Type::I64 => { |
|
| 100 | 100 | io::print("i64"); |
|
| 101 | 101 | } |
|
| 102 | - | case super::Type::Pointer { target, mutable } => { |
|
| 102 | + | case super::Type::Pointer { target, mutable, .. } => { |
|
| 103 | 103 | printPtrPrefix(mutable); |
|
| 104 | 104 | printTypeBody(*target, brief); |
|
| 105 | 105 | } |
|
| 106 | - | case super::Type::Slice { item, mutable } => { |
|
| 106 | + | case super::Type::Slice { item, mutable, .. } => { |
|
| 107 | 107 | printPtrPrefix(mutable); |
|
| 108 | 108 | io::print("["); |
|
| 109 | 109 | printTypeBody(*item, brief); |
|
| 110 | 110 | io::print("]"); |
|
| 111 | 111 | } |
| 146 | 146 | printNominalTypeName(info); |
|
| 147 | 147 | } else { |
|
| 148 | 148 | printNominalType(info); |
|
| 149 | 149 | } |
|
| 150 | 150 | } |
|
| 151 | - | case super::Type::TraitObject { traitInfo, mutable } => { |
|
| 151 | + | case super::Type::TraitObject { traitInfo, mutable, .. } => { |
|
| 152 | 152 | printPtrPrefix(mutable); |
|
| 153 | 153 | io::print("opaque "); |
|
| 154 | 154 | io::print(traitInfo.name); |
|
| 155 | 155 | } |
|
| 156 | 156 | case super::Type::Range { start, end } => { |
lib/std/lang/resolver/tests.rad
+2 -2
| 398 | 398 | } |
|
| 399 | 399 | ||
| 400 | 400 | fn expectSliceType(ty: super::Type, mutable: bool) -> super::Type |
|
| 401 | 401 | throws (testing::TestError) |
|
| 402 | 402 | { |
|
| 403 | - | let case super::Type::Slice { item, mutable: sliceMut } = ty |
|
| 403 | + | let case super::Type::Slice { item, mutable: sliceMut, .. } = ty |
|
| 404 | 404 | else throw testing::TestError::Failed; |
|
| 405 | 405 | try testing::expect(sliceMut == mutable); |
|
| 406 | 406 | ||
| 407 | 407 | return *item; |
|
| 408 | 408 | } |
|
| 409 | 409 | ||
| 410 | 410 | fn expectPointerType(ty: super::Type, mutable: bool) -> super::Type |
|
| 411 | 411 | throws (testing::TestError) |
|
| 412 | 412 | { |
|
| 413 | - | let case super::Type::Pointer { target, mutable: ptrMut } = ty |
|
| 413 | + | let case super::Type::Pointer { target, mutable: ptrMut, .. } = ty |
|
| 414 | 414 | else throw testing::TestError::Failed; |
|
| 415 | 415 | try testing::expect(ptrMut == mutable); |
|
| 416 | 416 | ||
| 417 | 417 | return *target; |
|
| 418 | 418 | } |
lib/std/lang/scanner.rad
+3 -2
| 103 | 103 | // Trait-related tokens. |
|
| 104 | 104 | Trait, Instance, |
|
| 105 | 105 | ||
| 106 | 106 | // Type-related tokens. |
|
| 107 | 107 | I8, I16, I32, I64, U8, U16, U32, U64, |
|
| 108 | - | Opaque, Fn, Bool, Union, Record, As |
|
| 108 | + | Opaque, Fn, Bool, Union, Record, As, Unsafe |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// A reserved keyword. |
|
| 112 | 112 | record Keyword { |
|
| 113 | 113 | /// Keyword string. |
| 115 | 115 | /// Corresponding token. |
|
| 116 | 116 | tok: TokenKind, |
|
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | 119 | /// Sorted keyword table for binary search. |
|
| 120 | - | constant KEYWORDS: [Keyword; 51] = [ |
|
| 120 | + | constant KEYWORDS: [Keyword; 52] = [ |
|
| 121 | 121 | { name: "align", tok: TokenKind::Align }, |
|
| 122 | 122 | { name: "and", tok: TokenKind::And }, |
|
| 123 | 123 | { name: "as", tok: TokenKind::As }, |
|
| 124 | 124 | { name: "assert", tok: TokenKind::Assert }, |
|
| 125 | 125 | { name: "bool", tok: TokenKind::Bool }, |
| 165 | 165 | { name: "u32", tok: TokenKind::U32 }, |
|
| 166 | 166 | { name: "u64", tok: TokenKind::U64 }, |
|
| 167 | 167 | { name: "u8", tok: TokenKind::U8 }, |
|
| 168 | 168 | { name: "undefined", tok: TokenKind::Undefined }, |
|
| 169 | 169 | { name: "union", tok: TokenKind::Union }, |
|
| 170 | + | { name: "unsafe", tok: TokenKind::Unsafe }, |
|
| 170 | 171 | { name: "use", tok: TokenKind::Use }, |
|
| 171 | 172 | { name: "while", tok: TokenKind::While }, |
|
| 172 | 173 | ]; |
|
| 173 | 174 | ||
| 174 | 175 | /// Describes where source code originated from. |
lib/std/lang/scanner/tests.rad
+5 -1
| 222 | 222 | try testing::expect(super::next(&mut s).kind == expectedKind); |
|
| 223 | 223 | } |
|
| 224 | 224 | } |
|
| 225 | 225 | ||
| 226 | 226 | @test fn testScanKeywords() throws (testing::TestError) { |
|
| 227 | - | let mut s = testScanner("nil mod not static"); |
|
| 227 | + | let mut s = testScanner("nil mod not static unsafe"); |
|
| 228 | 228 | let tok1: super::Token = super::next(&mut s); |
|
| 229 | 229 | ||
| 230 | 230 | try testing::expect(tok1.kind == super::TokenKind::Nil); |
|
| 231 | 231 | try testing::expect(tok1.source.len == 3); |
|
| 232 | 232 |
| 239 | 239 | try testing::expect(tok3.source.len == 3); |
|
| 240 | 240 | ||
| 241 | 241 | let tok4: super::Token = super::next(&mut s); |
|
| 242 | 242 | try testing::expect(tok4.kind == super::TokenKind::Static); |
|
| 243 | 243 | try testing::expect(tok4.source.len == 6); |
|
| 244 | + | ||
| 245 | + | let tok5: super::Token = super::next(&mut s); |
|
| 246 | + | try testing::expect(tok5.kind == super::TokenKind::Unsafe); |
|
| 247 | + | try testing::expect(tok5.source.len == 6); |
|
| 244 | 248 | } |
|
| 245 | 249 | ||
| 246 | 250 | @test fn testScanVoidAsIdent() throws (testing::TestError) { |
|
| 247 | 251 | let mut s = testScanner("void"); |
|
| 248 | 252 | let tok: super::Token = super::next(&mut s); |
lib/std/lang/types.rad
added
+11 -0
| 1 | + | //! Shared Radiance language types. |
|
| 2 | + | ||
| 3 | + | /// Ownership and safety class for pointer-like types. |
|
| 4 | + | export union PointerClass { |
|
| 5 | + | /// Owned pointer, eg. `*T`. |
|
| 6 | + | Owned, |
|
| 7 | + | /// Reference, borrowed pointer, eg. `&T`. |
|
| 8 | + | Ref, |
|
| 9 | + | /// Unsafe, raw pointer, eg. `*unsafe T`. |
|
| 10 | + | Unsafe, |
|
| 11 | + | } |
std.lib
+1 -0
| 20 | 20 | lib/std/arch/rv64/asm/scanner.rad |
|
| 21 | 21 | lib/std/arch/rv64/asm/parser.rad |
|
| 22 | 22 | lib/std/arch/rv64/asm/emit.rad |
|
| 23 | 23 | lib/std/lang.rad |
|
| 24 | 24 | lib/std/lang/alloc.rad |
|
| 25 | + | lib/std/lang/types.rad |
|
| 25 | 26 | lib/std/lang/strings.rad |
|
| 26 | 27 | lib/std/lang/sexpr.rad |
|
| 27 | 28 | lib/std/lang/ast.rad |
|
| 28 | 29 | lib/std/lang/ast/printer.rad |
|
| 29 | 30 | lib/std/lang/scanner.rad |
test/tests/builtin.size.align.rad
+0 -1
| 54 | 54 | assert @alignOf(*[u16]) == 8; |
|
| 55 | 55 | ||
| 56 | 56 | assert @sizeOf(*mut [u8]) == 16; |
|
| 57 | 57 | assert @alignOf(*mut [u8]) == 8; |
|
| 58 | 58 | ||
| 59 | - | ||
| 60 | 59 | return 0; |
|
| 61 | 60 | } |