Require unsafe functions for explicit slice construction

d3cc6c5fbbc2e0864036c767efba5b0ce50ce05eba9f8600fc1ec9e347431e32
Alexis Sellier committed ago 1 parent ceaf9422
lib/std/arch/rv64/emit.rad +1 -1
343 343
///
344 344
/// Called after each function.
345 345
///
346 346
/// Uses two-instruction sequences: short branches use `branch` and `nop`,
347 347
/// long branches use inverted branch  and `jal` or `auipc` and `jalr`.
348 -
export fn patchLocalBranches(e: &mut Emitter) {
348 +
export unsafe fn patchLocalBranches(e: &mut Emitter) {
349 349
    for i in 0..e.pendingBranches.len {
350 350
        let p = e.pendingBranches[i];
351 351
        let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE);
352 352
        match p.kind {
353 353
            case BranchKind::Cond { op, rs1, rs2 } => {
lib/std/lang/alloc.rad +1 -1
93 93
/// Allocate a slice of `count` elements, each of `size` bytes with given alignment.
94 94
///
95 95
/// Returns a type-erased slice that should be cast to the appropriate `*[T]`.
96 96
/// The slice length is set to `count` (element count, not bytes).
97 97
/// Throws `AllocError` if the arena is exhausted.
98 -
export fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
98 +
export unsafe fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) {
99 99
    if count == 0 {
100 100
        return &mut [];
101 101
    }
102 102
    let ptr = try alloc(arena, size * count, alignment);
103 103
lib/std/lang/lower.rad +2 -2
1946 1946
    return sym;
1947 1947
}
1948 1948
1949 1949
/// Remove the last block parameter and its associated variable.
1950 1950
/// Used when detecting a trivial phi that can be eliminated.
1951 -
fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) {
1951 +
unsafe fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) {
1952 1952
    let blk = getBlockMut(self, block);
1953 1953
    if blk.params.len > 0 {
1954 1954
        // TODO: Use `pop`?
1955 1955
        set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap);
1956 1956
    }
2868 2868
fn enterVarScope(self: &FnLowerer) -> u32 {
2869 2869
    return self.vars.len;
2870 2870
}
2871 2871
2872 2872
/// Restore lexical variable scope depth.
2873 -
fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) {
2873 +
unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) {
2874 2874
    set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap);
2875 2875
}
2876 2876
2877 2877
/// Get the metadata for a variable.
2878 2878
fn getVar(self: &FnLowerer, v: Var) -> *VarData {
lib/std/lang/resolver.rad +18 -17
1039 1039
    }
1040 1040
    return &errs[index];
1041 1041
}
1042 1042
1043 1043
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1044 -
fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1044 +
unsafe fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1045 1045
    // If our error list is full, just return an error without recording it.
1046 1046
    if self.errors.len >= self.errors.cap {
1047 1047
        return ResolveError::Failure;
1048 1048
    }
1049 1049
    // Don't record more than one error per node.
1056 1056
1057 1057
    return ResolveError::Failure;
1058 1058
}
1059 1059
1060 1060
/// Like [`emitError`], but for type mismatches specifically.
1061 -
fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1061 +
unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1062 1062
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1063 1063
}
1064 1064
1065 1065
/// Allocate a scope object with the given symbol capacity.
1066 1066
unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1156 1156
    }
1157 1157
    return Type::Never;
1158 1158
}
1159 1159
1160 1160
/// Require that loop control statements appear inside a loop.
1161 -
fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
1161 +
unsafe fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
1162 1162
    if self.loopDepth == 0 {
1163 1163
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1164 1164
    }
1165 1165
}
1166 1166
1196 1196
    set self.currentFn = nil;
1197 1197
    exitScope(self);
1198 1198
}
1199 1199
1200 1200
/// Extract the identifier text from a node.
1201 -
fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8]
1201 +
unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8]
1202 1202
    throws (ResolveError)
1203 1203
{
1204 1204
    let case ast::NodeValue::Ident(name) = node.value
1205 1205
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1206 1206
    return name;
2240 2240
2241 2241
    return sym;
2242 2242
}
2243 2243
2244 2244
/// Add a symbol to the given scope.
2245 -
fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2245 +
unsafe fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2246 2246
    for i in 0..scope.symbolsLen {
2247 2247
        if scope.symbols[i].name == sym.name {
2248 2248
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
2249 2249
        }
2250 2250
    }
2413 2413
}
2414 2414
2415 2415
/// Flatten an identifier or scope access chain into an array of name segments.
2416 2416
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
2417 2417
/// Return the number of segments written to the buffer.
2418 -
fn flattenPath(
2418 +
unsafe fn flattenPath(
2419 2419
    self: &mut Resolver,
2420 2420
    node: *ast::Node,
2421 2421
    buf: &mut [*[u8]]
2422 2422
) -> u32 throws (ResolveError) {
2423 2423
    let mut out: u32 = 0;
2629 2629
2630 2630
    return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
2631 2631
}
2632 2632
2633 2633
/// Recursively resolve the remaining path segments by traversing child modules.
2634 -
fn resolveModulePathRecursive(
2634 +
unsafe fn resolveModulePathRecursive(
2635 2635
    self: &mut Resolver,
2636 2636
    node: *ast::Node,
2637 2637
    path: &[*[u8]],
2638 2638
    sym: *Symbol
2639 2639
) -> ResolvedModule throws (ResolveError) {
2712 2712
        }
2713 2713
    }
2714 2714
}
2715 2715
2716 2716
/// Require the current function to be unsafe.
2717 -
fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
2717 +
unsafe fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) {
2718 2718
    if not self.inUnsafeFn {
2719 2719
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2720 2720
    }
2721 2721
}
2722 2722
2723 2723
/// Reject calls from safe code through unsafe function types.
2724 -
fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType)
2724 +
unsafe fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType)
2725 2725
    throws (ResolveError)
2726 2726
{
2727 2727
    if info.isUnsafe and not self.inUnsafeFn {
2728 2728
        throw emitError(self, node, ErrorKind::UnsafeCall);
2729 2729
    }
2779 2779
unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2780 2780
    return try visit(self, node, Type::Unknown);
2781 2781
}
2782 2782
2783 2783
/// Reject nested references while allowing a direct parameter reference.
2784 -
fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type)
2784 +
unsafe fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type)
2785 2785
    throws (ResolveError)
2786 2786
{
2787 2787
    if isRefType(ty) {
2788 2788
        if let case Type::Pointer { target, .. } = ty {
2789 2789
            if containsRef(*target) {
2798 2798
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2799 2799
    }
2800 2800
}
2801 2801
2802 2802
/// Require a type that may be stored or escape a call.
2803 -
fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type)
2803 +
unsafe fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type)
2804 2804
    throws (ResolveError)
2805 2805
{
2806 2806
    if containsRef(ty) {
2807 2807
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2808 2808
    }
3022 3022
    }
3023 3023
    return mask;
3024 3024
}
3025 3025
3026 3026
/// Ensure the `default` attribute is only applied to functions.
3027 -
fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32)
3027 +
unsafe fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32)
3028 3028
    throws (ResolveError)
3029 3029
{
3030 3030
    let defaultBit = ast::Attribute::Default as u32;
3031 3031
    if (attrs & defaultBit) <> 0 {
3032 3032
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
3286 3286
        try checkAssignable(self, arg, fieldType);
3287 3287
    }
3288 3288
}
3289 3289
3290 3290
/// Check that the argument count of a constructor pattern or call matches the record field count.
3291 -
fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3291 +
unsafe fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3292 3292
    if args.len <> recInfo.fields.len {
3293 3293
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch {
3294 3294
            expected: recInfo.fields.len as u32,
3295 3295
            actual: args.len,
3296 3296
        }));
4074 4074
4075 4075
/// Resolve a standalone method declaration (signature only).
4076 4076
/// Validates the receiver type and registers the method in the method table.
4077 4077
4078 4078
/// Extract the type name from a resolved receiver type node.
4079 -
fn receiverTypeName(
4079 +
unsafe fn receiverTypeName(
4080 4080
    self: &mut Resolver,
4081 4081
    receiverType: *ast::Node,
4082 4082
) -> *[u8] throws (ResolveError) {
4083 4083
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4084 4084
        receiverType.value
5395 5395
        };
5396 5396
        let _ = try checkAssignable(self, args[1], Type::U32);
5397 5397
        if args.len == 3 {
5398 5398
            let _ = try checkAssignable(self, args[2], Type::U32);
5399 5399
        }
5400 +
        try requireUnsafe(self, node);
5400 5401
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
5401 5402
    }
5402 5403
    if args.len <> 1 {
5403 5404
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5404 5405
            expected: 1,
5700 5701
        try checkIndex(self, end);
5701 5702
    }
5702 5703
}
5703 5704
5704 5705
/// Emit an error when a slice range with compile-tyime values exceeds the array length.
5705 -
fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5706 +
unsafe fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5706 5707
    let mut startVal: ?u32 = nil;
5707 5708
    let mut endVal: ?u32 = length;
5708 5709
5709 5710
    if let startNode = range.start {
5710 5711
        if let val = constSliceIndex(self, startNode) {
6057 6058
    });
6058 6059
    return setNodeType(self, node, arrayTy);
6059 6060
}
6060 6061
6061 6062
/// Resolve union variant access.
6062 -
fn resolveUnionVariantAccess(
6063 +
unsafe fn resolveUnionVariantAccess(
6063 6064
    self: &mut Resolver,
6064 6065
    node: *ast::Node,
6065 6066
    access: ast::Access,
6066 6067
    unionType: UnionType,
6067 6068
    variantName: *[u8]
6350 6351
    }
6351 6352
    return types::PointerClass::Ref;
6352 6353
}
6353 6354
6354 6355
/// Select an address type without extending the target storage lifetime.
6355 -
fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass
6356 +
unsafe fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass
6356 6357
    throws (ResolveError)
6357 6358
{
6358 6359
    if isUnsafePointerType(hint) {
6359 6360
        try requireUnsafe(self, target);
6360 6361
        return types::PointerClass::Unsafe;
lib/std/lang/resolver/tests.rad +25 -5
4135 4135
/// Test @sliceOf with correct arguments succeeds.
4136 4136
@test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) {
4137 4137
    // Immutable pointer.
4138 4138
    {
4139 4139
        let mut a = testResolver();
4140 -
        let program = "fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }";
4140 +
        let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }";
4141 4141
        let result = try resolveProgramStr(&mut a, program);
4142 4142
        try expectNoErrors(&result);
4143 4143
    }
4144 4144
    // Mutable pointer produces mutable slice.
4145 4145
    {
4146 4146
        let mut a = testResolver();
4147 -
        let program = "fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }";
4147 +
        let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }";
4148 4148
        let result = try resolveProgramStr(&mut a, program);
4149 4149
        try expectNoErrors(&result);
4150 4150
    }
4151 4151
}
4152 4152
4229 4229
4230 4230
/// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds.
4231 4231
@test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) {
4232 4232
    {
4233 4233
        let mut a = testResolver();
4234 -
        let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4234 +
        let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }";
4235 4235
        let result = try resolveProgramStr(&mut a, program);
4236 4236
        try expectNoErrors(&result);
4237 4237
    }
4238 4238
    // Mutable pointer produces mutable slice.
4239 4239
    {
4240 4240
        let mut a = testResolver();
4241 -
        let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }";
4241 +
        let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }";
4242 4242
        let result = try resolveProgramStr(&mut a, program);
4243 4243
        try expectNoErrors(&result);
4244 4244
    }
4245 4245
}
4246 4246
5358 5358
    try expectAnalyzeOk("fn run(value: *u32) -> *u32 { return &*value; }");
5359 5359
    try expectAnalyzeOk("fn run(values: *[u32]) -> *[u32] { return &values[..]; }");
5360 5360
    try expectAnalyzeOk("record Cell { value: u32 } fn run(value: *Cell) -> *u32 { return &value.value; }");
5361 5361
    try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return &values[0]; }");
5362 5362
    try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return values.ptr; }");
5363 -
    try expectAnalyzeOk("fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }");
5363 +
    try expectAnalyzeOk("unsafe fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }");
5364 5364
}
5365 5365
5366 5366
/// References are rejected from every nested or storable type position.
5367 5367
@test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) {
5368 5368
    {
5508 5508
    try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p as *opaque; }");
5509 5509
    try expectAnalyzeOk("fn run(s: *[u8]) -> *[opaque] { return s as *[opaque]; }");
5510 5510
    try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p as *u8; }");
5511 5511
}
5512 5512
5513 +
/// Explicit slice bounds require an unsafe function.
5514 +
@test unsafe fn testSliceConstructionRequiresUnsafe() throws (testing::TestError) {
5515 +
    let programs = &[
5516 +
        "fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }",
5517 +
        "fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }",
5518 +
        "fn run(p: &u8) -> u8 { return @sliceOf(p, 100)[99]; }",
5519 +
        "fn run(p: &mut u8) { set @sliceOf(p, 100)[99] = 1; }",
5520 +
        "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = @sliceOf(&DATA[0], 100); return s[99]; }",
5521 +
    ];
5522 +
    for program in programs {
5523 +
        let mut a = testResolver();
5524 +
        let result = try resolveProgramStr(&mut a, program);
5525 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5526 +
    }
5527 +
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }");
5528 +
    try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }");
5529 +
    try expectAnalyzeOk("unsafe fn run(p: &u8) -> u8 { return @sliceOf(p, 1)[0]; }");
5530 +
    try expectAnalyzeOk("fn run(s: *[u8]) -> *[u8] { return &s[..]; }");
5531 +
}
5532 +
5513 5533
/// Unsafe declarations may compose unsafe operations and calls.
5514 5534
@test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5515 5535
    let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }";
5516 5536
    try expectAnalyzeOk(program);
5517 5537
}
test/tests/slice.basic.rad +2 -2
18 18
    let c: *[i32] = s;
19 19
    return c;
20 20
}
21 21
22 22
/// Builds a slice header from a pointer and length.
23 -
fn sliceOf(ptr: *i32, len: u32) -> *[i32] {
23 +
unsafe fn sliceOf(ptr: *i32, len: u32) -> *[i32] {
24 24
    return @sliceOf(ptr, len);
25 25
}
26 26
27 27
/// Builds a slice header from pointer, length, and capacity.
28 -
fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] {
28 +
unsafe fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] {
29 29
    return @sliceOf(ptr, len, cap);
30 30
}
test/tests/slice.construct.unsafe.rad added +15 -0
1 +
//! returns: 0
2 +
3 +
/// Backing storage for explicit slice bounds.
4 +
static DATA: [u8; 3] = [11, 22, 33];
5 +
6 +
/// Construct valid slices from stored pointers in an unsafe function.
7 +
@default unsafe fn main() -> i32 {
8 +
    let s = @sliceOf(&DATA[0], 3);
9 +
    if s[2] <> 33 { return 1; }
10 +
    let t = @sliceOf(&mut DATA[0], 2, 3);
11 +
    if t.len <> 2 or t.cap <> 3 { return 2; }
12 +
    set t[1] = 44;
13 +
    if DATA[1] <> 44 { return 3; }
14 +
    return 0;
15 +
}
test/tests/slice.of.rad +2 -1
1 1
//! returns: 0
2 2
//! Test that @sliceOf produces a fat pointer with the correct length.
3 3
4 -
fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] {
4 +
/// Build a slice over the caller's initialized storage.
5 +
unsafe fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] {
5 6
    return @sliceOf(ptr, count);
6 7
}
7 8
8 9
@default unsafe fn main() -> i32 {
9 10
    let mut arr: [i32; 4] = [10, 20, 30, 40];