parser: Support `unsafe(expr)` (#5973)

pull/5940/head
Nick Treleaven 2020-07-28 09:20:52 +01:00 committed by GitHub
parent d7ed3cd8b7
commit 19c226fcf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 39 deletions

View File

@ -1907,10 +1907,10 @@ To mark potentially memory-unsafe operations, enclose them in an `unsafe` block:
```v ```v
// allocate 2 uninitialized bytes & return a reference to them // allocate 2 uninitialized bytes & return a reference to them
mut p := unsafe { &byte(malloc(2)) } mut p := unsafe(&byte(malloc(2)))
p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks
unsafe { unsafe {
p[0] = `h` p[0] = `h` // OK
p[1] = `i` p[1] = `i`
} }
p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks
@ -1920,13 +1920,12 @@ unsafe {
assert *p == `i` assert *p == `i`
``` ```
Best practice is to avoid putting memory-safe expressions inside an `unsafe` block, Best practice is to avoid putting memory-safe expressions inside an `unsafe` expression/block,
so that the reason for using `unsafe` is as clear as possible. Generally any code so that the reason for using `unsafe` is as clear as possible. Generally any code
you think is memory-safe should not be inside an `unsafe` block, so the compiler you think is memory-safe should be verified by the compiler.
can verify it.
If you suspect your program does violate memory-safety, you have a head start on If you suspect your program does violate memory-safety, you have a head start on
finding the cause: look at the `unsafe` blocks (and how they interact with finding the cause: look for the `unsafe` keyword (and how it affects the
surrounding code). surrounding code).
* Note: This is work in progress. * Note: This is work in progress.

View File

@ -728,6 +728,7 @@ pub:
pub struct ParExpr { pub struct ParExpr {
pub: pub:
expr Expr expr Expr
is_unsafe bool // unsafe(expr)
} }
pub struct GoStmt { pub struct GoStmt {

View File

@ -2374,7 +2374,14 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return table.void_type return table.void_type
} }
ast.ParExpr { ast.ParExpr {
return c.expr(node.expr) if !node.is_unsafe {
return c.expr(node.expr)
}
assert !c.inside_unsafe
c.inside_unsafe = true
t := c.expr(node.expr)
c.inside_unsafe = false
return t
} }
ast.RangeExpr { ast.RangeExpr {
// never happens // never happens

View File

@ -902,6 +902,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
panic('fmt: OrExpr should to linked to CallExpr') panic('fmt: OrExpr should to linked to CallExpr')
} }
ast.ParExpr { ast.ParExpr {
if node.is_unsafe {
f.write('unsafe')
}
f.write('(') f.write('(')
f.par_level++ f.par_level++
f.expr(node.expr) f.expr(node.expr)

View File

@ -602,13 +602,22 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
} }
} }
.key_unsafe { .key_unsafe {
p.next() // unsafe {
assert !p.inside_unsafe if p.peek_tok.kind == .lcbr {
p.inside_unsafe = true p.next()
stmts := p.parse_block() assert !p.inside_unsafe
p.inside_unsafe = false p.inside_unsafe = true
return ast.UnsafeStmt{ stmts := p.parse_block()
stmts: stmts p.inside_unsafe = false
return ast.UnsafeStmt{
stmts: stmts
}
}
// unsafe(
pos := p.tok.position()
return ast.ExprStmt{
expr: p.expr(0)
pos: pos
} }
} }
.hash { .hash {

View File

@ -93,12 +93,23 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
pos := p.tok.position() pos := p.tok.position()
assert !p.inside_unsafe assert !p.inside_unsafe
p.inside_unsafe = true p.inside_unsafe = true
stmts := p.parse_block() if p.tok.kind == .lpar {
p.inside_unsafe = false // unsafe(
node = ast.UnsafeExpr{ p.check(.lpar)
stmts: stmts node = ast.ParExpr{
pos: pos expr: p.expr(0)
is_unsafe: true
}
p.check(.rpar)
} else {
// unsafe {
// old syntax, UnsafeExpr can be removed later
node = ast.UnsafeExpr{
stmts: p.parse_block()
pos: pos
}
} }
p.inside_unsafe = false
} }
.key_lock, .key_rlock { .key_lock, .key_rlock {
node = p.lock_expr() node = p.lock_expr()

View File

@ -3,17 +3,9 @@ fn test_ptr_assign() {
mut p := &v[0] mut p := &v[0]
unsafe { unsafe {
(*p)++ (*p)++
} p++ // p now points to v[1]
unsafe {
p++
} // p now points to v[1]
unsafe {
(*p) += 2 (*p) += 2
} p += 2 // p now points to v[3]
unsafe {
p += 2
} // p now points to v[3]
unsafe {
*p = 31 *p = 31
} }
assert v[0] == 6 assert v[0] == 6
@ -24,16 +16,9 @@ fn test_ptr_assign() {
fn test_ptr_infix() { fn test_ptr_infix() {
v := 4 v := 4
mut q := unsafe { mut q := unsafe(&v - 1)
&v - 1 q = unsafe(q + 3)
} assert q == unsafe(&v + 2)
q = unsafe {
q + 3
}
_ := q
_ := v
} }
struct S1 { struct S1 {