Require unsafe functions for storage reinterpretation casts

ceaf9422d1bee3419c7e9b470171ed7f39e101188cad252b5a48753302fd8fa9
Alexis Sellier committed ago 1 parent 5807f724
compiler/radiance.rad +1 -1
578 578
        try lowerModuleTreeInto(ctx, &mut *low, graph, childId, false, pkg);
579 579
    }
580 580
}
581 581
582 582
/// Build a scope access chain: a::b::c from a slice of identifiers.
583 -
fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node {
583 +
unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node {
584 584
    let mut result = ast::synthNode(
585 585
        arena,
586 586
        ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[0]))
587 587
    );
588 588
    for i in 1..path.len {
lib/std/arch/rv64.rad +1 -1
297 297
        emit::emit(&mut generator.e, word);
298 298
    }
299 299
}
300 300
301 301
/// Finish RV64 code generation and return the emitted program.
302 -
export fn finishProgram(
302 +
export unsafe fn finishProgram(
303 303
    generator: &mut Generator,
304 304
    globalData: *[il::Data],
305 305
    storage: Storage,
306 306
    roDataPrefix: *[u8],
307 307
    roDataBuf: *mut [u8],
lib/std/arch/rv64/printer.rad +2 -2
42 42
unsafe fn write(out: &mut sexpr::Output, s: &[u8]) {
43 43
    sexpr::write(out, s);
44 44
}
45 45
46 46
/// Format `i32` into arena.
47 -
fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] {
47 +
unsafe fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] {
48 48
    let mut digits: [u8; 12] = undefined;
49 49
    let start = fmt::formatI32(val, &mut digits[..]);
50 50
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
51 51
    try! mem::copy(slice, &digits[start..]);
52 52
53 53
    return slice;
54 54
}
55 55
56 56
/// Format `u32` into arena.
57 -
fn formatU32(a: &mut alloc::Arena, val: u32) -> *[u8] {
57 +
unsafe fn formatU32(a: &mut alloc::Arena, val: u32) -> *[u8] {
58 58
    let mut digits: [u8; 10] = undefined;
59 59
    let start = fmt::formatU32(val, &mut digits[..]);
60 60
    let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8];
61 61
    try! mem::copy(slice, &digits[start..]);
62 62
lib/std/lang/ast.rad +3 -3
26 26
        nextId: 0,
27 27
    };
28 28
}
29 29
30 30
/// Create an empty `*mut [*Node]` slice with the given capacity.
31 -
export fn nodeSlice(arena: &mut NodeArena, capacity: u32) -> *mut [*Node] {
31 +
export unsafe fn nodeSlice(arena: &mut NodeArena, capacity: u32) -> *mut [*Node] {
32 32
    if capacity == 0 {
33 33
        return &mut [];
34 34
    }
35 35
    let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity);
36 36
823 823
        else => return false,
824 824
    }
825 825
}
826 826
827 827
/// Allocate a new AST node from the arena with the given span and value.
828 -
export fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
828 +
export unsafe fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *mut Node {
829 829
    let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node));
830 830
    let node = p as *mut Node;
831 831
    let nodeId = arena.nextId;
832 832
    set arena.nextId = nodeId + 1;
833 833
835 835
836 836
    return node;
837 837
}
838 838
839 839
/// Allocate a synthetic AST node with a zero-length span.
840 -
export fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *mut Node {
840 +
export unsafe fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *mut Node {
841 841
    return allocNode(arena, Span { offset: 0, length: 0 }, value);
842 842
}
843 843
844 844
/// Synthetic module with a single function in it.
845 845
record SynthFnMod: Copy {
lib/std/lang/ast/printer.rad +13 -13
82 82
        case types::PointerClass::Unsafe => return unsafeHead,
83 83
    }
84 84
}
85 85
86 86
/// Convert a type signature to an S-expression.
87 -
fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
87 +
unsafe fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr {
88 88
    match sig {
89 89
        case super::TypeSig::Void => return sexpr::sym("void"),
90 90
        case super::TypeSig::Opaque => return sexpr::sym("opaque"),
91 91
        case super::TypeSig::Bool => return sexpr::sym("bool"),
92 92
        case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)),
122 122
        }
123 123
    }
124 124
}
125 125
126 126
/// Convert a node slice to a slice of expressions.
127 -
fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
127 +
unsafe fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
128 128
    if nodes.len == 0 {
129 129
        return &[];
130 130
    }
131 131
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
132 132
    for node, i in nodes {
134 134
    }
135 135
    return buf;
136 136
}
137 137
138 138
/// Convert optional attributes to an attribute list expression.
139 -
fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
139 +
unsafe fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr {
140 140
    let mut exprs: *[sexpr::Expr] = &[];
141 141
    if let list = attrs {
142 142
        set exprs = nodeListToExprs(a, &list.list[..]);
143 143
    }
144 144
    return sexpr::list(a, "attrs", exprs);
145 145
}
146 146
147 147
/// Convert an optional node to an expression, or return placeholder.
148 -
fn toExprOpt(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
148 +
unsafe fn toExprOpt(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
149 149
    if let n = opt {
150 150
        return toExpr(a, n);
151 151
    }
152 152
    return sexpr::sym("_");
153 153
}
154 154
155 155
/// Convert an optional node to an expression, or return `Null`.
156 -
fn toExprOrNull(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
156 +
unsafe fn toExprOrNull(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr {
157 157
    if let n = opt {
158 158
        return toExpr(a, n);
159 159
    }
160 160
    return sexpr::Expr::Null;
161 161
}
162 162
163 163
/// Convert an optional guard.
164 -
fn guardExpr(a: &mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr {
164 +
unsafe fn guardExpr(a: &mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr {
165 165
    if let g = guard {
166 166
        return sexpr::list(a, "guard", &[toExpr(a, g)]);
167 167
    }
168 168
    return sexpr::Expr::Null;
169 169
}
170 170
171 171
/// Convert a list of match prongs to expressions.
172 -
fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
172 +
unsafe fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
173 173
    if nodes.len == 0 {
174 174
        return &[];
175 175
    }
176 176
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
177 177
    for prong, i in nodes {
186 186
    }
187 187
    return buf;
188 188
}
189 189
190 190
/// Convert a match prong to an S-expression.
191 -
fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
191 +
unsafe fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr {
192 192
    match p.arm {
193 193
        case super::ProngArm::Case(patterns) => {
194 194
            return sexpr::block(a, "case", &[
195 195
                sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])),
196 196
                guardExpr(a, p.guard)
207 207
        }
208 208
    }
209 209
}
210 210
211 211
/// Convert a record field declaration to an S-expression.
212 -
fn fieldToExpr(
212 +
unsafe fn fieldToExpr(
213 213
    a: &mut alloc::Arena,
214 214
    field: ?*super::Node,
215 215
    type: *super::Node,
216 216
    value: ?*super::Node
217 217
) -> sexpr::Expr {
218 218
    return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]);
219 219
}
220 220
221 221
/// Convert a list of record fields to expressions.
222 -
fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
222 +
unsafe fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
223 223
    if nodes.len == 0 {
224 224
        return &[];
225 225
    }
226 226
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
227 227
    for node, i in nodes {
236 236
    }
237 237
    return buf;
238 238
}
239 239
240 240
/// Convert a union variant to an S-expression.
241 -
fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
241 +
unsafe fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr {
242 242
    return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]);
243 243
}
244 244
245 245
/// Convert a list of union variants to expressions.
246 -
fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
246 +
unsafe fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] {
247 247
    if nodes.len == 0 {
248 248
        return &[];
249 249
    }
250 250
    let buf = try! sexpr::allocExprs(a, nodes.len as u32);
251 251
    for node, i in nodes {
260 260
    }
261 261
    return buf;
262 262
}
263 263
264 264
/// Convert an AST node to an S-expression.
265 -
export fn toExpr(a: &mut alloc::Arena, node: *super::Node) -> sexpr::Expr {
265 +
export unsafe fn toExpr(a: &mut alloc::Arena, node: *super::Node) -> sexpr::Expr {
266 266
    match node.value {
267 267
        case super::NodeValue::Placeholder => return sexpr::sym("_"),
268 268
        case super::NodeValue::Nil => return sexpr::sym("nil"),
269 269
        case super::NodeValue::Undef => return sexpr::sym("undefined"),
270 270
        case super::NodeValue::Bool(v) => {
lib/std/lang/gen/data.rad +2 -2
80 80
}
81 81
82 82
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
83 83
/// Iterates data requiring sidecar image bytes, serializing each data item.
84 84
/// Returns the total number of bytes written.
85 -
export fn emitSection(
85 +
export unsafe fn emitSection(
86 86
    items: *[il::Data],
87 87
    dataSymMap: &DataSymMap,
88 88
    fnLabels: &labels::Labels,
89 89
    codeBase: u32,
90 90
    buf: *mut [u8],
92 92
) -> u32 {
93 93
    return emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
94 94
}
95 95
96 96
/// Emit data bytes for a single section starting at `startOffset`.
97 -
export fn emitSectionAtOffset(
97 +
export unsafe fn emitSectionAtOffset(
98 98
    items: *[il::Data],
99 99
    dataSymMap: &DataSymMap,
100 100
    fnLabels: &labels::Labels,
101 101
    codeBase: u32,
102 102
    buf: *mut [u8],
lib/std/lang/gen/regalloc/assign.rad +2 -2
185 185
        usedCalleeSaved,
186 186
    };
187 187
}
188 188
189 189
/// Create an empty register map.
190 -
fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
190 +
unsafe fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
191 191
    let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32];
192 192
    let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg];
193 193
194 194
    return RegMap { virtRegs, physRegs, n: 0 };
195 195
}
270 270
    }
271 271
    panic "rallocReg: no free register, spilling fault";
272 272
}
273 273
274 274
/// Record the current index; forward traversal leaves the last operand use.
275 -
fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) {
275 +
unsafe fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) {
276 276
    recordLastUse(reg, ctxPtr as &mut LastUseCtx);
277 277
}
278 278
279 279
/// Record the last instruction that uses the register.
280 280
fn recordLastUse(reg: il::Reg, ctx: &mut LastUseCtx) {
lib/std/lang/gen/regalloc/liveness.rad +2 -2
179 179
        bitset::put(c.uses, reg.n);
180 180
    }
181 181
}
182 182
183 183
/// Callback for [`il::forEachReg`]: updates max register number.
184 -
fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) {
184 +
unsafe fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) {
185 185
    updateMaxReg(reg, ctx as &mut u32);
186 186
}
187 187
188 188
/// Update the largest register number.
189 189
fn updateMaxReg(reg: il::Reg, max: &mut u32) {
248 248
    il::forEachReg(instr, findRegCallback, &mut ctx as &mut opaque);
249 249
    return ctx.found;
250 250
}
251 251
252 252
/// Callback for [`il::forEachReg`]: sets found if register matches target.
253 -
fn findRegCallback(reg: il::Reg, ctx: &mut opaque) {
253 +
unsafe fn findRegCallback(reg: il::Reg, ctx: &mut opaque) {
254 254
    findReg(reg, ctx as &mut FindCtx);
255 255
}
256 256
257 257
/// Set the match flag when the register equals the target.
258 258
fn findReg(reg: il::Reg, c: &mut FindCtx) {
lib/std/lang/gen/regalloc/spill.rad +1 -1
310 310
        }
311 311
    }
312 312
}
313 313
314 314
/// Callback for [`il::forEachReg`]: increments use count for register.
315 -
fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) {
315 +
unsafe fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) {
316 316
    countRegUse(reg, ctxPtr as &mut CountCtx);
317 317
}
318 318
319 319
/// Add the block weight to the register use count.
320 320
fn countRegUse(reg: il::Reg, ctx: &mut CountCtx) {
lib/std/lang/il.rad +1 -1
80 80
81 81
/// Separator for qualified symbol names.
82 82
export constant PATH_SEPARATOR: *[u8] = "::";
83 83
84 84
/// Format a qualified symbol name: `pkg::mod::path::name`.
85 -
export fn formatQualifiedName(arena: &mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] {
85 +
export unsafe fn formatQualifiedName(arena: &mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] {
86 86
    let mut totalLen: u32 = name.len;
87 87
    for segment in path {
88 88
        set totalLen += segment.len + PATH_SEPARATOR.len;
89 89
    }
90 90
    let buf = try! alloc::allocSlice(arena, 1, 1, totalLen) as *mut [u8];
lib/std/lang/module/printer.rad +2 -2
5 5
use std::io;
6 6
use std::lang::sexpr;
7 7
use std::lang::alloc;
8 8
9 9
/// Format a u32 and allocate the result in the arena.
10 -
fn formatId(a: &mut alloc::Arena, id: u32) -> *[u8] {
10 +
unsafe fn formatId(a: &mut alloc::Arena, id: u32) -> *[u8] {
11 11
    let mut digits: [u8; 10] = undefined;
12 12
    let start = fmt::formatU32(id, &mut digits[..]);
13 13
    let ptr = try alloc::allocSlice(a, 1, 1, digits.len - start) catch { return "?"; };
14 14
    let slice = ptr as *mut [u8];
15 15
    try mem::copy(slice, &digits[start..]) catch { return "?"; };
27 27
        case super::ModuleState::Ready => return sexpr::sym("ready"),
28 28
    }
29 29
}
30 30
31 31
/// Recursively convert a module entry and its descendants to an S-expression.
32 -
fn subtreeToExpr(
32 +
unsafe fn subtreeToExpr(
33 33
    a: &mut alloc::Arena,
34 34
    graph: &super::ModuleGraph,
35 35
    entry: *super::ModuleEntry
36 36
) -> sexpr::Expr {
37 37
    let idText = formatId(a, entry.id as u32);
lib/std/lang/resolver.rad +30 -16
888 888
    ty: Type,
889 889
    next: ?*TypeNode,
890 890
}
891 891
892 892
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
893 -
export fn allocType(self: &mut Resolver, ty: Type) -> *Type {
893 +
export unsafe fn allocType(self: &mut Resolver, ty: Type) -> *Type {
894 894
    // Search existing types for a match.
895 895
    let mut cursor = self.types;
896 896
    while let node = cursor {
897 897
        if node.ty == ty {
898 898
            return &node.ty;
909 909
910 910
    return &node.ty;
911 911
}
912 912
913 913
/// Allocate a nominal type descriptor and return a pointer to it.
914 -
fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType {
914 +
unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType {
915 915
    // Nb. We don't attempt to de-duplicate nominal type entries,
916 916
    // since they don't carry node information and we create
917 917
    // placeholder entries when binding symbols.
918 918
    let entry = try! alloc::alloc(
919 919
        &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType)
923 923
924 924
    return entry;
925 925
}
926 926
927 927
/// Allocate a function type descriptor and return a pointer to it.
928 -
fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType {
928 +
unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType {
929 929
    let entry = try! alloc::alloc(
930 930
        &mut self.arena, @sizeOf(FnType), @alignOf(FnType)
931 931
    ) as *mut FnType;
932 932
933 933
    set *entry = info;
965 965
    /// Root AST node.
966 966
    rootAst: *ast::Node,
967 967
}
968 968
969 969
/// Construct a resolver with module context and backing storage.
970 -
export fn resolver(
970 +
export unsafe fn resolver(
971 971
    storage: ResolverStorage,
972 972
    config: Config
973 973
) -> Resolver {
974 974
    let mut arena = storage.arena;
975 975
    let symbols = try! alloc::allocSlice(
1061 1061
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 -
fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1066 +
unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1067 1067
    // Check for an existing scope for this node, and don't allocate a new
1068 1068
    // one in that case.
1069 1069
    if let scope = scopeFor(self, owner) {
1070 1070
        return scope;
1071 1071
    }
1085 1085
}
1086 1086
1087 1087
/// Enter a new local scope that is the child of the current scope.
1088 1088
/// This creates a parent/child relationship that means that lookups in the
1089 1089
/// child scope can recurse upwards.
1090 -
export fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope {
1090 +
export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope {
1091 1091
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1092 1092
    set scope.parent = self.scope;
1093 1093
    set self.scope = scope;
1094 1094
    return scope;
1095 1095
}
1096 1096
1097 1097
/// Enter a module scope. Returns an object that can be used to exit the scope.
1098 -
export fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1098 +
export unsafe fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1099 1099
    let prevScope = self.scope;
1100 1100
    let prevMod = self.currentMod;
1101 1101
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1102 1102
1103 1103
    set self.scope = scope;
1402 1402
    }
1403 1403
    return arg;
1404 1404
}
1405 1405
1406 1406
/// Allocate a new symbol, and return a reference to it.
1407 -
fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1407 +
unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1408 1408
    let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol;
1409 1409
    set *sym = Symbol { name, data, attrs, node, moduleId: nil };
1410 1410
1411 1411
    return sym;
1412 1412
}
2224 2224
    }
2225 2225
    return actualTy;
2226 2226
}
2227 2227
2228 2228
/// Bind an identifier in the given scope.
2229 -
fn bindIdent(
2229 +
unsafe fn bindIdent(
2230 2230
    self: &mut Resolver,
2231 2231
    name: *[u8],
2232 2232
    owner: *ast::Node,
2233 2233
    data: SymbolData,
2234 2234
    attrs: u32,
2290 2290
    }
2291 2291
    return sym;
2292 2292
}
2293 2293
2294 2294
/// Bind a constant identifier in the current scope.
2295 -
fn bindConstIdent(
2295 +
unsafe fn bindConstIdent(
2296 2296
    self: &mut Resolver,
2297 2297
    ident: *ast::Node,
2298 2298
    owner: *ast::Node,
2299 2299
    type: Type,
2300 2300
    val: ?ConstValue,
2311 2311
}
2312 2312
2313 2313
/// Bind a module identifier in the given scope.
2314 2314
/// This is used when declaring modules with `mod` or
2315 2315
/// importing modules with `use`.
2316 -
fn bindModuleIdent(
2316 +
unsafe fn bindModuleIdent(
2317 2317
    self: &mut Resolver,
2318 2318
    entry: *module::ModuleEntry,
2319 2319
    scope: *mut Scope,
2320 2320
    owner: *ast::Node,
2321 2321
    attrs: u32,
2326 2326
2327 2327
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
2328 2328
}
2329 2329
2330 2330
/// Bind a type identifier in the current scope.
2331 -
fn bindTypeIdent(
2331 +
unsafe fn bindTypeIdent(
2332 2332
    self: &mut Resolver,
2333 2333
    ident: *ast::Node,
2334 2334
    owner: *ast::Node,
2335 2335
    type: *mut NominalType,
2336 2336
    attrs: u32
3621 3621
3622 3622
    set *nominalTy = NominalType::Record(recordType);
3623 3623
}
3624 3624
3625 3625
/// Bind a type name.
3626 -
fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3626 +
unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3627 3627
    throws (ResolveError)
3628 3628
{
3629 3629
    let attrMask = resolveAttributes(self, attrs);
3630 3630
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3631 3631
3635 3635
3636 3636
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3637 3637
}
3638 3638
3639 3639
/// Allocate a trait type descriptor and return a pointer to it.
3640 -
fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType {
3640 +
unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType {
3641 3641
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3642 3642
    let entry = p as *mut TraitType;
3643 3643
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
3644 3644
3645 3645
    return entry;
3646 3646
}
3647 3647
3648 3648
/// Bind a trait name in the current scope.
3649 -
fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3649 +
unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3650 3650
    throws (ResolveError)
3651 3651
{
3652 3652
    let attrMask = resolveAttributes(self, attrs);
3653 3653
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3654 3654
6610 6610
                set valid = true;
6611 6611
            }
6612 6612
        }
6613 6613
    }
6614 6614
    if valid {
6615 +
        if let case Type::Pointer { target: sourceTarget, .. } = sourceTy {
6616 +
            if let case Type::Pointer { target: targetTarget, .. } = targetTy {
6617 +
                if *targetTarget <> Type::Opaque and not typesEqual(*sourceTarget, *targetTarget) {
6618 +
                    try requireUnsafe(self, node);
6619 +
                }
6620 +
            }
6621 +
        }
6622 +
        if let case Type::Slice { item: sourceItem, .. } = sourceTy {
6623 +
            if let case Type::Slice { item: targetItem, .. } = targetTy {
6624 +
                if *targetItem <> Type::Opaque and not typesEqual(*sourceItem, *targetItem) {
6625 +
                    try requireUnsafe(self, node);
6626 +
                }
6627 +
            }
6628 +
        }
6615 6629
        // Propagate the constant value after applying the cast's target-width
6616 6630
        // truncation and signed interpretation.
6617 6631
        if let value = constValueEntry(self, expr.value) {
6618 6632
            if let case ConstValue::Int(i) = value {
6619 6633
                setNodeConstValue(self, node, castConstInt(i, targetTy));
7390 7404
    }
7391 7405
}
7392 7406
7393 7407
/// Bind all type names in a module.
7394 7408
/// Skips declarations that have already been bound.
7395 -
fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7409 +
unsafe fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7396 7410
    for node in block.statements {
7397 7411
        match node.value {
7398 7412
            case ast::NodeValue::RecordDecl(decl) => {
7399 7413
                if symbolFor(self, node) == nil {
7400 7414
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
lib/std/lang/resolver/tests.rad +36 -9
3641 3641
// Opaque pointer tests.
3642 3642
3643 3643
/// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast.
3644 3644
@test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) {
3645 3645
    let mut a = testResolver();
3646 -
    let result = try resolveProgramStr(&mut a, "fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }");
3646 +
    let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }");
3647 3647
    try expectNoErrors(&result);
3648 3648
}
3649 3649
3650 3650
/// You cannot assign an opaque pointer to a non-opaque pointer without a cast.
3651 3651
@test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) {
3692 3692
}
3693 3693
3694 3694
/// Test that you can dereference after casting.
3695 3695
@test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) {
3696 3696
    let mut a = testResolver();
3697 -
    let result = try resolveProgramStr(&mut a, "fn f() { let o: *opaque = undefined; let x = *(o as *i32); }");
3697 +
    let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }");
3698 3698
    try expectNoErrors(&result);
3699 3699
}
3700 3700
3701 3701
/// You cannot do pointer arithmetic with an opaque pointer.
3702 3702
@test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) {
4502 4502
        let mut a = testResolver();
4503 4503
        let result = try resolveBlockStr(&mut a, "let f: fn() = undefined; f as u32;");
4504 4504
        try expectNoErrors(&result);
4505 4505
    } { // *u8 to *i32 (u8 to i32 is valid).
4506 4506
        let mut a = testResolver();
4507 -
        let result = try resolveBlockStr(&mut a, "let p: *u8 = undefined; p as *i32;");
4507 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }");
4508 4508
        try expectNoErrors(&result);
4509 4509
    } { // **u8 to **i32 (*u8 to *i32 is valid).
4510 4510
        let mut a = testResolver();
4511 -
        let result = try resolveBlockStr(&mut a, "let p: **u8 = undefined; p as **i32;");
4511 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }");
4512 4512
        try expectNoErrors(&result);
4513 4513
    }
4514 4514
4515 4515
    { // *[i32] to *[opaque].
4516 4516
        let mut a = testResolver();
4517 4517
        let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *[opaque];");
4518 4518
        try expectNoErrors(&result);
4519 4519
    } { // *[opaque] to *[i32].
4520 4520
        let mut a = testResolver();
4521 -
        let result = try resolveBlockStr(&mut a, "let s: *[opaque] = undefined; s as *[i32];");
4521 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }");
4522 4522
        try expectNoErrors(&result);
4523 4523
    }
4524 4524
4525 4525
    { // *[i32] to *[u8].
4526 4526
        let mut a = testResolver();
4527 -
        let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *[u8];");
4527 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }");
4528 4528
        try expectNoErrors(&result);
4529 4529
    } { // *[record] to *[u8].
4530 4530
        let mut a = testResolver();
4531 -
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[u8]; }");
4531 +
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }");
4532 4532
        try expectNoErrors(&result);
4533 4533
    }
4534 4534
4535 4535
    { // *[u8] to *[i32].
4536 4536
        let mut a = testResolver();
4537 -
        let result = try resolveBlockStr(&mut a, "let s: *[u8] = undefined; s as *[i32];");
4537 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }");
4538 4538
        try expectNoErrors(&result);
4539 4539
    } { // *[*u8] to *[*i32]
4540 4540
        let mut a = testResolver();
4541 -
        let result = try resolveBlockStr(&mut a, "let s: *[*u8] = undefined; s as *[*i32];");
4541 +
        let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }");
4542 4542
        try expectNoErrors(&result);
4543 4543
    }
4544 4544
4545 4545
    { // Identity cast: *mut [i32] to *mut [i32].
4546 4546
        let mut a = testResolver();
5481 5481
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return 1 + p; }");
5482 5482
    try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut u8 { return p - 1; }");
5483 5483
    try expectAnalyzeOk("fn run(value: u32) -> u32 { return value + 1; }");
5484 5484
}
5485 5485
5486 +
/// Casts that reinterpret storage require an unsafe function.
5487 +
@test unsafe fn testStorageCastsRequireUnsafe() throws (testing::TestError) {
5488 +
    let programs = &[
5489 +
        "fn run(p: *u8) -> *u64 { return p as *u64; }",
5490 +
        "fn run(p: &u8) -> u64 { return *(p as &u64); }",
5491 +
        "fn run(p: *mut u8) -> *mut u64 { return p as *mut u64; }",
5492 +
        "fn run(p: *opaque) -> *u64 { return p as *u64; }",
5493 +
        "fn run(p: **u8) -> **u64 { return p as **u64; }",
5494 +
        "fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }",
5495 +
        "fn run(s: *[opaque]) -> *[u64] { return s as *[u64]; }",
5496 +
        "fn run(s: &mut [u64]) { let _ = s as &mut [u8]; }",
5497 +
        "static DATA: [u8; 1] = [42]; fn run() -> u64 { return *(&DATA[0] as *u64); }",
5498 +
    ];
5499 +
    for program in programs {
5500 +
        let mut a = testResolver();
5501 +
        let result = try resolveProgramStr(&mut a, program);
5502 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5503 +
    }
5504 +
    try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u64 { return p as *u64; }");
5505 +
    try expectAnalyzeOk("unsafe fn run(p: &u8) -> u64 { return *(p as &u64); }");
5506 +
    try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }");
5507 +
    try expectAnalyzeOk("fn run(p: *u8) -> *u8 { return p as *u8; }");
5508 +
    try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p as *opaque; }");
5509 +
    try expectAnalyzeOk("fn run(s: *[u8]) -> *[opaque] { return s as *[opaque]; }");
5510 +
    try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p as *u8; }");
5511 +
}
5512 +
5486 5513
/// Unsafe declarations may compose unsafe operations and calls.
5487 5514
@test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5488 5515
    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); }";
5489 5516
    try expectAnalyzeOk(program);
5490 5517
}
lib/std/lang/sexpr.rad +5 -5
31 31
    /// A block with a name, inline items, and child statements on separate lines.
32 32
    Block { name: *[u8], items: *[Expr], children: *[Expr] },
33 33
}
34 34
35 35
/// Allocate an array of Expr in the arena.
36 -
export fn allocExprs(arena: &mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) {
36 +
export unsafe fn allocExprs(arena: &mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) {
37 37
    if len == 0 {
38 38
        throw alloc::AllocError::OutOfMemory;
39 39
    }
40 40
    let ptr = try alloc::allocSlice(arena, @sizeOf(Expr), @alignOf(Expr), len);
41 41
    return ptr as *mut [Expr];
42 42
}
43 43
44 44
/// Allocate and copy items into the arena.
45 -
export fn allocItems(a: &mut alloc::Arena, items: &[Expr]) -> *[Expr] {
45 +
export unsafe fn allocItems(a: &mut alloc::Arena, items: &[Expr]) -> *[Expr] {
46 46
    if items.len == 0 {
47 47
        return &[];
48 48
    }
49 49
    let buf = try! allocExprs(a, items.len);
50 50
    for item, i in items {
62 62
export fn str(s: *[u8]) -> Expr {
63 63
    return Expr::Str(s);
64 64
}
65 65
66 66
/// Shorthand for creating a list.
67 -
export fn list(a: &mut alloc::Arena, head: *[u8], tail: &[Expr]) -> Expr {
67 +
export unsafe fn list(a: &mut alloc::Arena, head: *[u8], tail: &[Expr]) -> Expr {
68 68
    return Expr::List { head, tail: allocItems(a, tail), multiline: false };
69 69
}
70 70
71 71
/// Shorthand for creating a bracket-delimited vector.
72 -
export fn vec(a: &mut alloc::Arena, items: &[Expr]) -> Expr {
72 +
export unsafe fn vec(a: &mut alloc::Arena, items: &[Expr]) -> Expr {
73 73
    return Expr::Vec { items: allocItems(a, items) };
74 74
}
75 75
76 76
/// Shorthand for creating a block with inline items and child expressions.
77 -
export fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr {
77 +
export unsafe fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr {
78 78
    return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) };
79 79
}
80 80
81 81
/// Write a string to the output target.
82 82
export unsafe fn write(out: &mut Output, s: &[u8]) {
lib/std/vec.rad +2 -2
76 76
77 77
/// Pop an element from the end of the vector.
78 78
///
79 79
/// Copies the element into the provided output pointer.
80 80
/// Returns false if the vector is empty.
81 -
export fn pop(vec: &mut RawVec, out: &mut opaque) -> bool {
81 +
export unsafe fn pop(vec: &mut RawVec, out: &mut opaque) -> bool {
82 82
    if vec.len == 0 {
83 83
        return false;
84 84
    }
85 85
    set vec.len -= 1;
86 86
91 91
}
92 92
93 93
/// Set the element at the given index.
94 94
///
95 95
/// Returns false if index is out of bounds.
96 -
export fn put(vec: &mut RawVec, index: u32, elem: &opaque) -> bool {
96 +
export unsafe fn put(vec: &mut RawVec, index: u32, elem: &opaque) -> bool {
97 97
    if index >= vec.len {
98 98
        return false;
99 99
    }
100 100
    let off: u32 = index * vec.stride;
101 101
    copyBytes(&mut vec.data[off..off + vec.stride], @sliceOf(elem as &u8, vec.stride));
test/tests/cast.basic.rad +1 -1
37 37
fn i32ToI16(x: i32) -> i16 {
38 38
    return x as i16;
39 39
}
40 40
41 41
/// Cast pointer types (same size, no instruction).
42 -
fn ptrCast(x: *u8) -> *i32 {
42 +
unsafe fn ptrCast(x: *u8) -> *i32 {
43 43
    return x as *i32;
44 44
}
test/tests/fn.callback.nested.rad +8 -4
20 20
/// Update the maximum register count through a borrowed counter.
21 21
fn updateMax(reg: Reg, max: &mut u32) {
22 22
    set *max = maxRegNum(reg.n, *max);
23 23
}
24 24
25 -
fn maxRegCallback(reg: Reg, ctx: &mut opaque) {
25 +
/// Update a register counter through an opaque context.
26 +
unsafe fn maxRegCallback(reg: Reg, ctx: &mut opaque) {
26 27
    updateMax(reg, ctx as &mut u32);
27 28
}
28 29
29 -
fn withReg(val: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) {
30 +
/// Call the callback for a register value.
31 +
unsafe fn withReg(val: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
30 32
    if let case Val::Reg(r) = val {
31 33
        callback(r, ctx);
32 34
    }
33 35
}
34 36
35 -
fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) {
37 +
/// Visit the register values with an opaque context.
38 +
unsafe fn forEachVal(a: Val, b: Val, c: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) {
36 39
    withReg(a, callback, ctx);
37 40
    withReg(b, callback, ctx);
38 41
    withReg(c, callback, ctx);
39 42
}
40 43
41 -
@default fn main() -> i32 {
44 +
/// Exercise a callback that calls another function.
45 +
@default unsafe fn main() -> i32 {
42 46
    let mut maxReg: u32 = 0;
43 47
44 48
    forEachVal(
45 49
        Val::Reg(Reg { n: 0 }),
46 50
        Val::Reg(Reg { n: 5 }),
test/tests/pointer.cast.unsafe.rad added +16 -0
1 +
//! returns: 0
2 +
3 +
/// Aligned storage for pointer and slice views.
4 +
static DATA: [u64; 1] = [42];
5 +
6 +
/// Reinterpret valid storage in an unsafe function.
7 +
@default unsafe fn main() -> i32 {
8 +
    let bytes = &mut DATA[..] as *mut [u8];
9 +
    if bytes[0] <> 42 { return 1; }
10 +
    set bytes[0] = 43;
11 +
    let word = bytes.ptr as *u64;
12 +
    if *word <> 43 { return 2; }
13 +
    let words = bytes as *[u64];
14 +
    if words[0] <> 43 { return 3; }
15 +
    return 0;
16 +
}
test/tests/slice.append.rad +3 -3
23 23
    return base as *mut opaque;
24 24
}
25 25
26 26
/// Allocator record matching the compiler's expected layout.
27 27
record Allocator: Copy {
28 -
    func: fn(*mut opaque, u32, u32) -> *mut opaque,
28 +
    func: unsafe fn(*mut opaque, u32, u32) -> *mut opaque,
29 29
    ctx: *mut opaque,
30 30
}
31 31
32 -
fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque {
32 +
unsafe fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque {
33 33
    let arena = ctx as *mut Arena;
34 34
    return arenaAlloc(arena, size, al);
35 35
}
36 36
37 37
fn arenaAllocator(arena: *mut Arena) -> Allocator {
41 41
    };
42 42
}
43 43
44 44
static BUF: [u8; 4096] = undefined;
45 45
46 -
@default fn main() -> i32 {
46 +
@default unsafe fn main() -> i32 {
47 47
    static arena: Arena = undefined;
48 48
    set arena = newArena(&mut BUF[..]);
49 49
    let a = arenaAllocator(&mut arena);
50 50
51 51
    // Allocate initial capacity of 4.