compiler: Support direct cell borrows

958fd2e0432760e79d436e6e04e6d9ad64570fe0b4476da0f10b93c8d74e551e
Alexis Sellier committed ago 1 parent 2a67faea
compiler/radiance.rad +1 -1
772 772
    }
773 773
    let arrayLit = ast::synthNode(arena, ast::NodeValue::ArrayLit(elements));
774 774
775 775
    // Build: `&[...]`.
776 776
    let testsRef = ast::synthNode(arena, ast::NodeValue::AddressOf(ast::AddressOf {
777 -
        target: arrayLit, mutable: false,
777 +
        target: arrayLit, kind: ast::AddressKind::Shared,
778 778
    }));
779 779
780 780
    // Build: `testing::runAllTests(&[...])`.
781 781
    let runFn = synthScopeAccess(arena, &["testing", "runAllTests"]);
782 782
    let callArgs = ast::nodeSlice(arena, 1).append(testsRef, a);
lib/std/lang/ast.rad +17 -2
269 269
    returnType: ?*Node,
270 270
    /// Throwable type nodes declared in the signature.
271 271
    throwList: *[*Node],
272 272
}
273 273
274 +
/// Access provided by an address-of expression.
275 +
export union AddressKind: Copy {
276 +
    /// Shared access to an immutable value.
277 +
    Shared,
278 +
    /// Exclusive access to a mutable value.
279 +
    Mutable,
280 +
    /// Shared access to a mutable Copy payload.
281 +
    Cell,
282 +
}
283 +
274 284
/// Address-of expression metadata.
275 285
export record AddressOf: Copy {
276 286
    /// Target expression being referenced.
277 287
    target: *Node,
278 -
    /// Indicates whether the reference is mutable.
279 -
    mutable: bool,
288 +
    /// Access provided by the resulting pointer.
289 +
    kind: AddressKind,
290 +
}
291 +
292 +
/// Return whether creating an address requires exclusive source access.
293 +
export fn isExclusiveAddress(address: AddressOf) -> bool {
294 +
    return address.kind <> AddressKind::Shared;
280 295
}
281 296
282 297
/// Compound statement block with optional dedicated scope.
283 298
export record Block: Copy {
284 299
    /// Statements that belong to this block.
lib/std/lang/ast/printer.rad +2 -1
305 305
        case super::NodeValue::FieldAccess(acc) =>
306 306
            return sexpr::list(a, ".", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
307 307
        case super::NodeValue::ScopeAccess(acc) =>
308 308
            return sexpr::list(a, "::", &[toExpr(a, acc.parent), toExpr(a, acc.child)]),
309 309
        case super::NodeValue::AddressOf(addr) =>
310 -
            return sexpr::list(a, "&mut", &[toExpr(a, addr.target)]) if addr.mutable
310 +
            return sexpr::list(a, "&cell", &[toExpr(a, addr.target)]) if addr.kind == super::AddressKind::Cell
311 +
                else sexpr::list(a, "&mut", &[toExpr(a, addr.target)]) if addr.kind == super::AddressKind::Mutable
311 312
                else sexpr::list(a, "&", &[toExpr(a, addr.target)]),
312 313
        case super::NodeValue::Deref(target) =>
313 314
            return sexpr::list(a, "deref", &[toExpr(a, target)]),
314 315
        case super::NodeValue::As(cast) =>
315 316
            return sexpr::list(a, "as", &[toExpr(a, cast.value), toExpr(a, cast.type)]),
lib/std/lang/module.rad +1 -1
334 334
        children: [0; MAX_MODULES],
335 335
        childrenLen: 0,
336 336
        ast: nil,
337 337
        source: nil,
338 338
    };
339 -
    let updates = state as *cell ModuleUpdates;
339 +
    let updates = &cell *state;
340 340
341 341
    // TODO: This is a common pattern that needs better syntax.
342 342
    let m = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleEntry), @alignOf(ModuleEntry)) as *mut ModuleEntry;
343 343
    set *m = ModuleEntry {
344 344
        id: idx as u16,
lib/std/lang/parser.rad +17 -5
468 468
            let value = try parseUnaryExpr(p);
469 469
            return node(p, ast::NodeValue::Deref(value));
470 470
        }
471 471
        case scanner::TokenKind::Amp => {
472 472
            advance(p);
473 -
            let mutable = consume(p, scanner::TokenKind::Mut);
473 +
            let kind = parseAddressKind(p);
474 474
            let target = try parseUnaryExpr(p);
475 -
            return node(p, ast::NodeValue::AddressOf({ target: target, mutable: mutable }));
475 +
            return node(p, ast::NodeValue::AddressOf({ target, kind }));
476 476
        }
477 477
        else => {
478 478
            return try parsePrimary(p);
479 479
        }
480 480
    }
481 481
}
482 482
483 +
/// Parse the access qualifier after an address operator.
484 +
unsafe fn parseAddressKind(p: &mut Parser) -> ast::AddressKind {
485 +
    if consume(p, scanner::TokenKind::Mut) {
486 +
        return ast::AddressKind::Mutable;
487 +
    }
488 +
    if check(p, scanner::TokenKind::Ident) and mem::eq(p.current.source, "cell") {
489 +
        advance(p);
490 +
        return ast::AddressKind::Cell;
491 +
    }
492 +
    return ast::AddressKind::Shared;
493 +
}
494 +
483 495
/// Find the operator info for a token if it has precedence greater than the
484 496
/// given minimum.
485 497
fn findNextOp(kind: scanner::TokenKind, minPrec: i32) -> ?OpInfo {
486 498
    if let opInfo = getOpInfo(kind) {
487 499
        if opInfo.prec > minPrec {
1637 1649
    let mut region = node(p, ast::NodeValue::Region { name: regionName, parent: nil });
1638 1650
    let mut bindings = ast::nodeSlice(p.arena, 4);
1639 1651
    loop {
1640 1652
        try expect(p, scanner::TokenKind::Equal, "expected `=` after region binding");
1641 1653
        try expect(p, scanner::TokenKind::Amp, "expected `&` before borrowed place");
1642 -
        let mutable = consume(p, scanner::TokenKind::Mut);
1654 +
        let kind = parseAddressKind(p);
1643 1655
        let target = try parseUnaryExpr(p);
1644 -
        let value = node(p, ast::NodeValue::AddressOf({ target, mutable }));
1656 +
        let value = node(p, ast::NodeValue::AddressOf({ target, kind }));
1645 1657
        bindings.append(node(p, ast::NodeValue::RegionBinding({ label: binding, value })), p.allocator);
1646 1658
        if not consume(p, scanner::TokenKind::Comma) {
1647 1659
            break;
1648 1660
        }
1649 1661
        set binding = try parseIdent(p, "expected region binding name");
1677 1689
1678 1690
/// Parse an allocation session with an implicit exclusive source borrow.
1679 1691
unsafe fn parseSessionBlock(p: &mut Parser) -> *ast::Node throws (ParseError) {
1680 1692
    try expect(p, scanner::TokenKind::Use, "expected `use`");
1681 1693
    let target = try parseUnaryExpr(p);
1682 -
    let value = node(p, ast::NodeValue::AddressOf({ target, mutable: true }));
1694 +
    let value = node(p, ast::NodeValue::AddressOf({ target, kind: ast::AddressKind::Mutable }));
1683 1695
    try expect(p, scanner::TokenKind::As, "expected `as` after allocation source");
1684 1696
    let binding = try parseIdent(p, "expected allocation binding after `as`");
1685 1697
    let region = sessionRegion(p, binding);
1686 1698
    try expect(p, scanner::TokenKind::In, "expected `in` after allocation binding");
1687 1699
    let bindings = ast::nodeSlice(p.arena, 1).append(
lib/std/lang/parser/tests.rad +15 -5
2231 2231
    }
2232 2232
}
2233 2233
2234 2234
/// Test parsing reference (address-of) expressions.
2235 2235
@test unsafe fn testParseRefs() throws (testing::TestError) {
2236 +
    {
2237 +
        let expr = try! parseExprStr("&cell obj.field");
2238 +
        let case ast::NodeValue::AddressOf(address) = expr.value
2239 +
            else throw testing::TestError::Failed;
2240 +
        try testing::expect(address.kind == ast::AddressKind::Cell);
2241 +
        let case ast::NodeValue::FieldAccess(access) = address.target.value
2242 +
            else throw testing::TestError::Failed;
2243 +
        try expectIdent(access.parent, "obj");
2244 +
        try expectIdent(access.child, "field");
2245 +
    }
2236 2246
    {
2237 2247
        let refExpr = try! parseExprStr("&foo");
2238 2248
        let case ast::NodeValue::AddressOf(refNode) = refExpr.value
2239 2249
            else throw testing::TestError::Failed;
2240 -
        try testing::expect(refNode.mutable == false);
2250 +
        try testing::expect(refNode.kind == ast::AddressKind::Shared);
2241 2251
        try expectIdent(refNode.target, "foo");
2242 2252
    }
2243 2253
    {
2244 2254
        let mutRefExpr = try! parseExprStr("&mut bar");
2245 2255
        let case ast::NodeValue::AddressOf(mutRefNode) = mutRefExpr.value
2246 2256
            else throw testing::TestError::Failed;
2247 -
        try testing::expect(mutRefNode.mutable == true);
2257 +
        try testing::expect(mutRefNode.kind == ast::AddressKind::Mutable);
2248 2258
        try expectIdent(mutRefNode.target, "bar");
2249 2259
    }
2250 2260
    {
2251 2261
        let refFieldExpr = try! parseExprStr("&obj.field");
2252 2262
        let case ast::NodeValue::AddressOf(refFieldNode) = refFieldExpr.value
2253 2263
            else throw testing::TestError::Failed;
2254 -
        try testing::expect(refFieldNode.mutable == false);
2264 +
        try testing::expect(refFieldNode.kind == ast::AddressKind::Shared);
2255 2265
        let case ast::NodeValue::FieldAccess(access) = refFieldNode.target.value
2256 2266
            else throw testing::TestError::Failed;
2257 2267
        try expectIdent(access.parent, "obj");
2258 2268
        try expectIdent(access.child, "field");
2259 2269
    }
2260 2270
    {
2261 2271
        let mutRefFieldExpr = try! parseExprStr("&mut obj.field");
2262 2272
        let case ast::NodeValue::AddressOf(mutRefFieldNode) = mutRefFieldExpr.value
2263 2273
            else throw testing::TestError::Failed;
2264 -
        try testing::expect(mutRefFieldNode.mutable == true);
2274 +
        try testing::expect(mutRefFieldNode.kind == ast::AddressKind::Mutable);
2265 2275
        let case ast::NodeValue::FieldAccess(access) = mutRefFieldNode.target.value
2266 2276
            else throw testing::TestError::Failed;
2267 2277
        try expectIdent(access.parent, "obj");
2268 2278
        try expectIdent(access.child, "field");
2269 2279
    }
3087 3097
    let case ast::NodeValue::RegionBinding(arenaBinding) = arenaBindings[0].value
3088 3098
        else throw testing::TestError::Failed;
3089 3099
    let arenaLabel = arenaBinding.label else throw testing::TestError::Failed;
3090 3100
    let case ast::NodeValue::Ident(arenaLabelName) = arenaLabel.value
3091 3101
        else throw testing::TestError::Failed;
3092 -
    let case ast::NodeValue::AddressOf({ target: arenaTarget, mutable: true }) = arenaBinding.value.value
3102 +
    let case ast::NodeValue::AddressOf({ target: arenaTarget, kind: ast::AddressKind::Mutable }) = arenaBinding.value.value
3093 3103
        else throw testing::TestError::Failed;
3094 3104
    let case ast::NodeValue::Ident(arenaTargetName) = arenaTarget.value
3095 3105
        else throw testing::TestError::Failed;
3096 3106
    try testing::expect(
3097 3107
        arenaSession and arenaBindings.len == 1
lib/std/lang/resolver.rad +90 -13
3972 3972
        if not types::regionContains(source, region) {
3973 3973
            throw emitError(self, node, ErrorKind::RegionParent(region.name));
3974 3974
        }
3975 3975
    }
3976 3976
    match ty {
3977 +
        case Type::Cell { payload, .. } =>
3978 +
            return Type::Cell { class: types::PointerClass::Region(region), payload },
3977 3979
        case Type::Pointer { target, mutable, .. } =>
3978 3980
            return Type::Pointer { class: types::PointerClass::Region(region), target, mutable },
3979 3981
        case Type::Slice { item, mutable, .. } =>
3980 3982
            return Type::Slice { class: types::PointerClass::Region(region), item, mutable },
3981 3983
        else => throw emitError(self, node, ErrorKind::RefBinding),
4084 4086
            break;
4085 4087
        }
4086 4088
    }
4087 4089
    let selected = allocInstance
4088 4090
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4089 -
    if not address.mutable or not ast::isPlaceExpr(address.target)
4091 +
    if address.kind <> ast::AddressKind::Mutable or not ast::isPlaceExpr(address.target)
4090 4092
        or borrowPlace(self, address.target).root == nil
4091 4093
    {
4092 4094
        throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4093 4095
    }
4094 4096
    let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject {
8555 8557
        }
8556 8558
    }
8557 8559
    return false;
8558 8560
}
8559 8561
8562 +
/// Return whether an expression creates shared mutable access to a source place.
8563 +
fn createsCellBorrow(node: *ast::Node) -> bool {
8564 +
    match node.value {
8565 +
        case ast::NodeValue::AddressOf(address) => return address.kind == ast::AddressKind::Cell,
8566 +
        case ast::NodeValue::As(expr) => return createsCellBorrow(expr.value),
8567 +
        case ast::NodeValue::RegionApply { value, .. } => return createsCellBorrow(value),
8568 +
        else => return false,
8569 +
    }
8570 +
}
8571 +
8572 +
/// Find the exclusive handle that owns an addressed place.
8573 +
unsafe fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node {
8574 +
    let mut parent: *ast::Node = undefined;
8575 +
    match node.value {
8576 +
        case ast::NodeValue::Deref(target) => set parent = target,
8577 +
        case ast::NodeValue::FieldAccess(access) => set parent = access.parent,
8578 +
        case ast::NodeValue::Subscript { container, .. } => set parent = container,
8579 +
        else => return nil,
8580 +
    }
8581 +
    if let ty = typeFor(self, parent) {
8582 +
        match ty {
8583 +
            case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parent,
8584 +
            else => {}
8585 +
        }
8586 +
    }
8587 +
    return addressOwner(self, parent);
8588 +
}
8589 +
8560 8590
/// Analyze an address-of expression.
8561 8591
unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
8562 8592
    throws (ResolveError)
8563 8593
{
8564 8594
    if try isCellPayloadPlace(self, addr.target) {
8565 8595
        throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8566 8596
    }
8567 -
    if addr.mutable {
8597 +
    if addr.kind == ast::AddressKind::Cell and not ast::isPlaceExpr(addr.target) {
8598 +
        throw emitError(self, addr.target, ErrorKind::RefBinding);
8599 +
    }
8600 +
    if ast::isExclusiveAddress(addr) {
8568 8601
        if not try canBorrowMutFrom(self, addr.target) {
8569 8602
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8570 8603
        }
8571 8604
    }
8572 8605
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
8573 8606
        if let case ast::NodeValue::Range(range) = index.value {
8607 +
            if addr.kind == ast::AddressKind::Cell {
8608 +
                throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
8609 +
            }
8574 8610
            let containerTy = try infer(self, container);
8575 8611
            let subjectTy = autoDeref(containerTy);
8576 8612
8577 8613
            try checkSliceRangeIndices(self, range);
8578 8614
8579 8615
            let mut item: *Type = undefined;
8580 8616
            let mut capacity: ?u32 = nil;
8581 8617
8582 8618
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
8583 -
                if addr.mutable and not sliceMutable {
8619 +
                if ast::isExclusiveAddress(addr) and not sliceMutable {
8584 8620
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8585 8621
                }
8586 8622
                set item = sliceItem;
8587 8623
            } else {
8588 8624
                match subjectTy {
8595 8631
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
8596 8632
                    }
8597 8633
                }
8598 8634
            }
8599 8635
            let class = try addressClass(self, addr.target, hint);
8600 -
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
8636 +
            let sliceTy = Type::Slice { class, item, mutable: addr.kind == ast::AddressKind::Mutable };
8601 8637
            let alloc = allocType(self, sliceTy);
8602 8638
            setSliceRangeInfo(self, node, SliceRangeInfo {
8603 8639
                itemType: item,
8604 -
                mutable: addr.mutable,
8640 +
                mutable: addr.kind == ast::AddressKind::Mutable,
8605 8641
                capacity,
8606 8642
            });
8607 8643
            setNodeType(self, addr.target, *alloc);
8608 8644
            return setNodeType(self, node, *alloc);
8609 8645
        }
8627 8663
                else => {}
8628 8664
            }
8629 8665
        }
8630 8666
    }
8631 8667
8668 +
    if addr.kind == ast::AddressKind::Cell {
8669 +
        try validateCellPayload(self, node, targetTy);
8670 +
        if let case types::PointerClass::Region(region) = class {
8671 +
            try validateRegionStorage(self, addr.target, targetTy, region);
8672 +
        } else if class == types::PointerClass::Owned and containsRegion(targetTy) {
8673 +
            throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
8674 +
        }
8675 +
        return setNodeType(self, node, Type::Cell { class, payload: allocType(self, targetTy) });
8676 +
    }
8632 8677
    if let case Type::Array(arrayInfo) = targetTy {
8633 8678
        match addr.target.value {
8634 8679
            case ast::NodeValue::ArrayLit(_),
8635 8680
                 ast::NodeValue::ArrayRepeatLit(_) =>
8636 8681
            {
8637 -
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.mutable };
8682 +
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.kind == ast::AddressKind::Mutable };
8638 8683
                return setNodeType(self, node, *allocType(self, sliceTy));
8639 8684
            }
8640 8685
            else => {}
8641 8686
        }
8642 8687
    }
8643 8688
    let pointerTy = Type::Pointer {
8644 -
        class, target: allocType(self, targetTy), mutable: addr.mutable,
8689 +
        class, target: allocType(self, targetTy), mutable: addr.kind == ast::AddressKind::Mutable,
8645 8690
    };
8646 8691
    return setNodeType(self, node, pointerTy);
8647 8692
}
8648 8693
8649 8694
/// Analyze a dereference expression.
10089 10134
    }
10090 10135
    if checker.regionalLen >= MAX_REGIONAL_LOANS {
10091 10136
        throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow);
10092 10137
    }
10093 10138
    let index = checker.regionalLen;
10094 -
    set checker.regional[index] = RegionalLoan { source: node, region, place, exclusive: address.mutable };
10139 +
    set checker.regional[index] = RegionalLoan { source: node, region, place, exclusive: ast::isExclusiveAddress(address) };
10095 10140
    set checker.regionalLen += 1;
10096 10141
    set env.regionalLoans |= (1 as u64) << (index as u64);
10097 10142
}
10098 10143
10099 10144
/// Return whether an initializer copies a shared reference with a named region.
10260 10305
    }
10261 10306
    if checker.localLen >= MAX_LINEAR_BINDINGS {
10262 10307
        throw emitError(checker.resolver, node, ErrorKind::Internal);
10263 10308
    }
10264 10309
    let sym = symbolFor(checker.resolver, node) else panic "reference without binding";
10265 -
    let mut exclusive = isExclusiveArgument(ty);
10310 +
    let mut exclusive = isExclusiveArgument(ty) or createsCellBorrow(binding.value);
10266 10311
    if let case Type::Cell { .. } = ty {
10267 10312
        if let case ast::NodeValue::As(expr) = binding.value.value {
10268 10313
            if let source = typeFor(checker.resolver, expr.value) {
10269 10314
                if let case Type::Pointer { mutable: true, .. } = source {
10270 10315
                    set exclusive = true;
10673 10718
        }
10674 10719
    }
10675 10720
10676 10721
    for arg, i in call.args {
10677 10722
        let expected = *info.paramTypes[i];
10678 -
        let argExclusive = isExclusiveArgument(expected);
10723 +
        let argExclusive = isExclusiveArgument(expected) or createsCellBorrow(arg);
10679 10724
        if argExclusive {
10680 10725
            try checkPatternLoan(checker, arg);
10681 10726
        }
10682 10727
        let place = borrowPlace(checker.resolver, arg);
10683 10728
        if not isUnsafePointerType(expected) {
10913 10958
        }
10914 10959
        else => panic "checkLinearLoopPass: expected loop",
10915 10960
    }
10916 10961
}
10917 10962
10963 +
/// Transfer a cell's owning handle and check each address operand once.
10964 +
unsafe fn checkCellAddressOwner 'arena 'checking (
10965 +
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
10966 +
    node: *ast::Node, owner: *ast::Node
10967 +
) throws (ResolveError) where 'arena: 'checking {
10968 +
    if node == owner {
10969 +
        try checkLinearNode(checker, env, node, LinearUse::Consume);
10970 +
        return;
10971 +
    }
10972 +
    match node.value {
10973 +
        case ast::NodeValue::Deref(target) => try checkCellAddressOwner(checker, env, target, owner),
10974 +
        case ast::NodeValue::FieldAccess(access) => try checkCellAddressOwner(checker, env, access.parent, owner),
10975 +
        case ast::NodeValue::Subscript { container, index } => {
10976 +
            try checkCellAddressOwner(checker, env, container, owner);
10977 +
            try checkLinearNode(checker, env, index, LinearUse::Consume);
10978 +
        }
10979 +
        else => panic "cell address owner must belong to its place",
10980 +
    }
10981 +
}
10982 +
10918 10983
/// Check one expression or statement under an ownership-use context.
10919 10984
unsafe fn checkLinearNode 'arena 'checking (
10920 10985
    checker: &mut LinearChecker 'arena 'checking,
10921 10986
    env: &mut LinearEnv,
10922 10987
    node: *ast::Node,
11053 11118
        }
11054 11119
        case ast::NodeValue::RegionApply { value, .. } =>
11055 11120
            try checkLinearNode(checker, env, value, usage),
11056 11121
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
11057 11122
        case ast::NodeValue::AddressOf(addr) => {
11058 -
            try checkLocalLoans(checker, env, addr.target, addr.mutable);
11059 -
            if addr.mutable {
11123 +
            try checkLocalLoans(checker, env, addr.target, ast::isExclusiveAddress(addr));
11124 +
            if ast::isExclusiveAddress(addr) {
11060 11125
                try checkPatternLoan(checker, addr.target);
11061 11126
            }
11062 -
            try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
11127 +
            let mut owner: ?*ast::Node = nil;
11128 +
            if addr.kind == ast::AddressKind::Cell {
11129 +
                if let ty = typeFor(checker.resolver, node) {
11130 +
                    if let case Type::Cell { class: types::PointerClass::Owned, .. } = ty {
11131 +
                        set owner = addressOwner(checker.resolver, addr.target);
11132 +
                    }
11133 +
                }
11134 +
            }
11135 +
            if let source = owner {
11136 +
                try checkCellAddressOwner(checker, env, addr.target, source);
11137 +
            } else {
11138 +
                try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
11139 +
            }
11063 11140
            try addRegionalLoan(checker, env, node, addr);
11064 11141
        }
11065 11142
        case ast::NodeValue::Deref(target) => {
11066 11143
            if let resultTy = typeFor(checker.resolver, node) {
11067 11144
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
lib/std/lang/resolver/tests.rad +1 -1
6961 6961
@test unsafe fn testStackPointerRejected() throws (testing::TestError) {
6962 6962
    let programs = &[
6963 6963
        "fn run() { let value: u32 = 0; let pointer: *u32 = &value; }",
6964 6964
        "fn run() -> *u32 { let value: u32 = 0; return &value; }",
6965 6965
        "fn run(value: u32) -> *u32 { return &value; }",
6966 -
        "record Cell { value: u32 } fn run() { let cell = Cell { value: 1 }; let pointer: *u32 = &cell.value; }",
6966 +
        "record Cell { value: u32 } fn run() { let cell = Cell { value: 1 }; let pointer: *u32 = &(cell.value); }",
6967 6967
        "fn run() { let values = [1, 2]; let pointer: *i32 = &values[0]; }",
6968 6968
        "fn run() { let values = [1, 2]; let slice: *[i32] = &values[..]; }",
6969 6969
        "fn run(value: i32) { let slice: *[i32] = &[value]; }",
6970 6970
        "fn run(value: &u32) -> *u32 { return value; }",
6971 6971
        "fn run(value: &u32) -> *u32 { return &*value; }",
lib/std/lang/resolver/tests/regions.rad +75 -0
985 985
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload);
986 986
        }
987 987
    }
988 988
}
989 989
990 +
/// Direct cell borrows support Copy values, scoped loans, and call arguments.
991 +
@test unsafe fn testCellBorrow() throws (testing::TestError) {
992 +
    for program in [
993 +
        "fn f() -> u32 { let mut n: u32 = 0; { let c = &cell n; let alias = c; set *c = 1; set *alias += 1; } return n; }",
994 +
        "fn read(c: &cell u32) { set *c = 1; } fn f(p: &mut u32) { read(&cell *p); set *p = 2; }",
995 +
        "fn f() { let mut n: u32 = 0; let c: 'r = &cell n in { let alias = c; set *alias = 1; } set n = 2; }",
996 +
        "record R: Copy { a: u32, b: u32 } fn f() { let mut r = R { a: 0, b: 0 }; let a = &cell r.a; let b = &cell r.b; set *a = 1; set *b = 2; }",
997 +
        "fn f() { let mut a: [u32; 2] = [0, 0]; let c = &cell a; set *c = [1, 2]; let snapshot = *c; }",
998 +
        "fn forward(p: *mut u32) -> *mut u32 { return p; } fn f(p: *mut u32) -> *cell u32 { return &cell *forward(p); }",
999 +
        "record R: Copy { n: u32 } fn f(p: *mut R) -> *cell u32 { return &cell p.n; }",
1000 +
        "fn f(p: *mut [u32]) -> *cell u32 { return &cell p[0]; }",
1001 +
    ] {
1002 +
        let mut arena = super::testArena();
1003 +
        let storage: 'test = &mut arena in {
1004 +
            let mut res = super::testResolver(storage);
1005 +
            let result = try super::resolveProgramStr(&mut res, program);
1006 +
            try super::expectNoErrors(&result);
1007 +
        }
1008 +
    }
1009 +
}
1010 +
1011 +
/// Cell creation requires exclusive access for the full borrow duration.
1012 +
@test unsafe fn testCellBorrowConflicts() throws (testing::TestError) {
1013 +
    for program in [
1014 +
        "fn f() { let mut n: u32 = 0; let c = &cell n; let r = &n; }",
1015 +
        "unsafe fn f() { let mut n: u32 = 0; let c = &cell n; let r = &n; }",
1016 +
        "fn f() { let mut n: u32 = 0; let r = &n; let c = &cell n; }",
1017 +
        "fn f() { let mut n: u32 = 0; let c = &cell n; set n = 1; }",
1018 +
        "fn f() { let mut n: u32 = 0; let c = &cell n; let other = &cell n; }",
1019 +
        "fn f() { let mut n: u32 = 0; let c = &cell n as &cell u32; let r = &n; }",
1020 +
        "fn inspect(c: &cell u32, r: &u32) {} fn f() { let mut n: u32 = 0; inspect(&cell n, &n); }",
1021 +
        "fn inspect(r: &u32, c: &cell u32) {} fn f() { let mut n: u32 = 0; inspect(&n, &cell n); }",
1022 +
    ] {
1023 +
        let mut arena = super::testArena();
1024 +
        let storage: 'test = &mut arena in {
1025 +
            let mut res = super::testResolver(storage);
1026 +
            let result = try super::resolveProgramStr(&mut res, program);
1027 +
            let error = try super::expectError(&result);
1028 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
1029 +
        }
1030 +
    }
1031 +
}
1032 +
1033 +
/// Cell creation rejects immutable places and non-Copy payloads.
1034 +
@test unsafe fn testCellBorrowPayload() throws (testing::TestError) {
1035 +
    for program in [
1036 +
        "fn f() { let n: u32 = 0; let c = &cell n; }",
1037 +
        "fn f(p: &u32) { let c = &cell *p; }",
1038 +
        "unsafe fn f(p: &u32) { let c = &cell *p; }",
1039 +
    ] {
1040 +
        let mut arena = super::testArena();
1041 +
        let storage: 'test = &mut arena in {
1042 +
            let mut res = super::testResolver(storage);
1043 +
            let result = try super::resolveProgramStr(&mut res, program);
1044 +
            try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding);
1045 +
        }
1046 +
    }
1047 +
    for program in [
1048 +
        "record R { n: u32 } fn f() { let mut r = R { n: 0 }; let c = &cell r; }",
1049 +
        "fn f(p: &mut *mut u32) { let c = &cell *p; }",
1050 +
        "fn f(p: &mut [u32]) { let c = &cell p[..]; }",
1051 +
    ] {
1052 +
        let mut arena = super::testArena();
1053 +
        let storage: 'test = &mut arena in {
1054 +
            let mut res = super::testResolver(storage);
1055 +
            let result = try super::resolveProgramStr(&mut res, program);
1056 +
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload);
1057 +
        }
1058 +
    }
1059 +
}
1060 +
990 1061
/// Cell access cannot expose payload references or restore exclusive pointers.
991 1062
@test unsafe fn testCellPointerAccess() throws (testing::TestError) {
992 1063
    for program in [
993 1064
        "fn f(p: *cell u32) { let r = &*p; }",
994 1065
        "unsafe fn f(p: *cell u32) { let r = &*p; }",
1034 1105
}
1035 1106
1036 1107
/// Cell conversion consumes exclusive access, including call arguments.
1037 1108
@test unsafe fn testCellPointerOwnership() throws (testing::TestError) {
1038 1109
    for program in [
1110 +
        "fn f(p: *mut u32) { let c = &cell *p; set *p = 1; }",
1111 +
        "unsafe fn f(p: *mut u32) { let c = &cell *p; set *p = 1; }",
1112 +
        "record R: Copy { n: u32 } fn f(p: *mut R) { let c = &cell p.n; set p.n = 1; }",
1113 +
        "fn f(p: *mut [u32]) { let c = &cell p[0]; set p[0] = 1; }",
1039 1114
        "fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }",
1040 1115
        "unsafe fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }",
1041 1116
        "fn read(p: &cell u32) {} fn f(p: &mut u32) { read(p as &cell u32); set *p = 1; }",
1042 1117
    ] {
1043 1118
        let mut testArena60 = super::testArena();
seed/radiance.rv64 +0 -0

Binary file changed.

seed/radiance.rv64.git +1 -1
1 -
4a24fa5e2f0b82d0aa1f922caa85dcf831d22cb8828365820197f8ebb13f55b4
1 +
2a67faea10bc90ec2c810c94e06b752a2e30905084878116431fed5bc6668aea
test/tests/pointer.borrow.unsafe.rad +1 -1
61 61
    fill(rawValues);
62 62
    assert sum(rawValues) == 30;
63 63
    assert sum(@sliceOf(&values[0] as *unsafe u32, 2)) == 30;
64 64
65 65
    let cell = Cell { value: 7 };
66 -
    let rawCell: *unsafe Cell = &cell;
66 +
    let rawCell: *unsafe Cell = &(cell);
67 67
    assert inspect(rawCell) == 7;
68 68
    let object: *unsafe opaque Reader = rawCell;
69 69
    assert inspect(object) == 7;
70 70
    return 0;
71 71
}
test/tests/regions.cell.graph.rad +1 -1
34 34
    static DATA: [u8; 1024] = [0; 1024];
35 35
    let mut arena = alloc::new(&mut DATA[..]);
36 36
    let mut result: u32 = 0;
37 37
    use arena as graph in {
38 38
        let initial = try! graph.new(State 'graph::Pending);
39 -
        let state = initial as &'graph cell State 'graph;
39 +
        let state = &cell *initial;
40 40
        let body: &'graph Body 'graph = try! graph.new(Body 'graph { state });
41 41
        set *state = State 'graph::Ready(body);
42 42
        set result = snapshot(state);
43 43
    }
44 44
    alloc::reset(&mut arena);
test/tests/regions.cell.pointer.rad +6 -6
12 12
fn unionCell 'r (allocation: &Session 'r) -> u32 {
13 13
    let value = try! allocation.new(19 as u32);
14 14
    let shared: &'r u32 = value;
15 15
    let initial: Link 'r = Link::Value(shared);
16 16
    let storage = try! allocation.new(initial);
17 -
    let pointer = storage as &'r cell Link 'r;
17 +
    let pointer = &cell *storage;
18 18
    let snapshot = *pointer;
19 19
    set *pointer = Link::Empty;
20 20
    match *pointer {
21 21
        case Link::Empty => {
22 22
        },
29 29
}
30 30
31 31
fn local() -> u32 {
32 32
    let mut value: u32 = 0;
33 33
    {
34 -
        let pointer = &mut value as &cell u32;
34 +
        let pointer = &cell value;
35 35
        let alias = pointer;
36 36
        set *alias = 7;
37 37
        assert *pointer == 7;
38 38
        set *pointer = 8;
39 39
        assert *alias == 8;
61 61
@default fn main() -> u32 {
62 62
    assert local() == 7;
63 63
    assert @sizeOf(*cell u32) == @sizeOf(*mut u32);
64 64
    static VALUE: u32 = 0;
65 65
    let pointer: *mut u32 = &mut VALUE;
66 -
    let cell = pointer as *cell u32;
66 +
    let cell = &cell *pointer;
67 67
    let alias = cell;
68 68
    update(alias);
69 69
    assert *cell == 20;
70 70
    static CALLS: u32 = 0;
71 -
    let calls = &mut CALLS as *cell u32;
71 +
    let calls = &cell CALLS;
72 72
    set *select(cell, calls) += 1;
73 73
    assert *calls == 1;
74 74
    assert *cell == 21;
75 75
    set *cell = 20;
76 76
77 77
    static DATA: [u8; 256] = [0; 256];
78 78
    let mut arena = alloc::new(&mut DATA[..]);
79 79
    use arena as objects in {
80 80
        assert unionCell(&objects) == 19;
81 81
        let value = try! objects.new(22 as u32);
82 -
        let shared = value as &'objects cell u32;
82 +
        let shared = &cell *value;
83 83
        let optional: ?&'objects cell u32 = shared;
84 84
        if let present = optional {
85 85
            assert *present == 22;
86 86
        } else {
87 87
            panic;
92 92
        let copy = entry;
93 93
        set *copy.index = 21;
94 94
        assert read(entry.index) == 21;
95 95
        set *shared = 22;
96 96
        let pair = try! objects.new(Pair { a: 1, b: 2 });
97 -
        let pairCell = pair as &'objects cell Pair;
97 +
        let pairCell = &cell *pair;
98 98
        let snapshot = *pairCell;
99 99
        set *pairCell = Pair { a: 3, b: 4 };
100 100
        assert snapshot.a == 1 and snapshot.b == 2;
101 101
        assert (*pairCell).b == 4;
102 102
        assert replace(*pairCell, pairCell) == 3;
test/tests/regions.cell.recursive.rad +1 -1
12 12
@default unsafe fn main() -> u32 {
13 13
    static DATA: [u8; 256] = [0; 256];
14 14
    let mut arena = alloc::new(&mut DATA[..]);
15 15
    use arena as graph in {
16 16
        let initial = try! graph.new(Node 'graph::End);
17 -
        let node = initial as &'graph cell Node 'graph;
17 +
        let node = &cell *initial;
18 18
        set *node = Node 'graph::Link(node);
19 19
        let snapshot = *node;
20 20
        match snapshot {
21 21
            case Node 'graph::Link(alias) => set *alias = Node 'graph::End,
22 22
            case Node 'graph::End => panic,
test/tests/regions.compiler.symbol.rad +1 -1
19 19
@default fn main() -> u32 {
20 20
    static DATA: [u8; 256] = [0; 256];
21 21
    let mut arena = alloc::new(&mut DATA[..]);
22 22
    use arena as graph in {
23 23
        let index = try! graph.new(0 as u32);
24 -
        let description: Symbol 'graph = Symbol { name: "value", index: index as &'graph cell u32 };
24 +
        let description: Symbol 'graph = Symbol { name: "value", index: &cell *index };
25 25
        let owned = try! graph.new(description);
26 26
        let shared: &'graph Symbol 'graph = owned;
27 27
        let byName = try! graph.fill(shared, 2);
28 28
        let byId = try! graph.fill(shared, 2);
29 29
        assert byName[0] == byId[1];
test/tests/regions.copy.binding.rad +1 -3
49 49
    let a: 'slices = &xs[..], b = &ys[..] in {
50 50
        assert followSlice(a, b) == 22;
51 51
    }
52 52
    let mut x: u32 = 0;
53 53
    let mut y: u32 = 0;
54 -
    let a: 'cells = &mut x, b = &mut y in {
55 -
        let firstCell = a as &'cells cell u32;
56 -
        let secondCell = b as &'cells cell u32;
54 +
    let firstCell: 'cells = &cell x, secondCell = &cell y in {
57 55
        followCell(firstCell, secondCell);
58 56
        assert *firstCell == 20;
59 57
        assert *secondCell == 22;
60 58
    }
61 59
    assert x == 20;