parser: support parsing unsafe (as first token) as UnsafeExpr (#6032)

pull/6428/head
Nick Treleaven 2020-09-19 17:18:36 +01:00 committed by GitHub
parent 1c886ad067
commit 9958881cbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 104 additions and 69 deletions

View File

@ -2075,7 +2075,7 @@ To mark potentially memory-unsafe operations, enclose them in an `unsafe` block:
mut p := unsafe { &byte(malloc(2)) }
p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks
unsafe {
p[0] = `h`
p[0] = `h` // OK
p[1] = `i`
}
p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks

View File

@ -491,7 +491,7 @@ pub mut:
pub struct UnsafeExpr {
pub:
stmts []Stmt
expr Expr
pos token.Position
}
@ -1082,6 +1082,15 @@ pub fn (expr Expr) is_lvalue() bool {
return false
}
pub fn (expr Expr) is_expr() bool {
match expr {
IfExpr {return expr.is_expr}
MatchExpr {return expr.is_expr}
else {}
}
return true
}
pub fn (stmt Stmt) position() token.Position {
match stmt {
AssertStmt { return stmt.pos }

View File

@ -264,7 +264,7 @@ pub fn (x Expr) str() string {
return '_likely_(${it.expr.str()})'
}
UnsafeExpr {
return 'unsafe { $it.stmts.len stmts }'
return 'unsafe { $it.expr }'
}
else {}
}

View File

@ -3101,30 +3101,11 @@ pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
}
pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) table.Type {
slen := node.stmts.len
if slen > 1 {
c.error('FIXME: unsafe expression block should support multiple statements', node.pos)
return table.none_type
}
if slen == 0 {
c.error('unsafe expression does not yield an expression', node.pos)
return table.none_type
}
assert !c.inside_unsafe
c.inside_unsafe = true
defer {
c.inside_unsafe = false
}
if slen > 1 {
c.stmts(node.stmts[0..slen - 1])
}
last := node.stmts[0]
if last is ast.ExprStmt {
t := c.expr(last.expr)
return t
}
c.error('unsafe expression does not yield an expression', node.pos)
return table.none_type
t := c.expr(node.expr)
c.inside_unsafe = false
return t
}
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {

View File

@ -1075,8 +1075,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
}
ast.UnsafeExpr {
f.write('unsafe {')
es := node.stmts[0] as ast.ExprStmt
f.expr(es.expr)
f.expr(node.expr)
f.write('}')
}
}

View File

@ -2224,8 +2224,7 @@ fn (mut g Gen) expr(node ast.Expr) {
g.write(')')
}
ast.UnsafeExpr {
es := node.stmts[0] as ast.ExprStmt
g.expr(es.expr)
g.expr(node.expr)
}
}
}

View File

@ -655,8 +655,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
// TODO
}
ast.UnsafeExpr {
es := node.stmts[0] as ast.ExprStmt
g.expr(es.expr)
g.expr(node.expr)
}
}
}

View File

@ -623,27 +623,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
}
}
.key_unsafe {
if p.peek_tok.kind == .lcbr {
// unsafe {
p.next()
assert !p.inside_unsafe
p.inside_unsafe = true
stmts := p.parse_block()
p.inside_unsafe = false
return ast.Block{
stmts: stmts
is_unsafe: true
}
} else {
p.error_with_pos('please use `unsafe {`', p.tok.position())
}
// unsafe( ; NB: this will be never reached
pos := p.tok.position()
ex := p.expr(0)
return ast.ExprStmt{
expr: ex
pos: pos
}
return p.unsafe_stmt()
}
.hash {
return p.hash()
@ -1988,3 +1968,55 @@ pub fn (mut p Parser) mark_var_as_used(varname string) bool {
}
return false
}
fn (mut p Parser) unsafe_stmt() ast.Stmt {
pos := p.tok.position()
p.next()
if p.tok.kind != .lcbr {
p.error_with_pos('please use `unsafe {`', p.tok.position())
}
p.next()
assert !p.inside_unsafe
if p.tok.kind == .rcbr {
// `unsafe {}`
p.next()
return ast.Block{
is_unsafe: true
}
}
p.inside_unsafe = true
p.open_scope() // needed in case of `unsafe {stmt}`
defer {
p.inside_unsafe = false
p.close_scope()
}
stmt := p.stmt(false)
if p.tok.kind == .rcbr {
if stmt is ast.ExprStmt {
// `unsafe {expr}`
if stmt.expr.is_expr() {
p.next()
ue := ast.UnsafeExpr{
expr: stmt.expr
pos: pos
}
// parse e.g. `unsafe {expr}.foo()`
expr := p.expr_with_left(ue, 0, p.is_stmt_ident)
return ast.ExprStmt{
expr: expr
pos: pos
}
}
}
}
// unsafe {stmts}
mut stmts := [stmt]
for p.tok.kind != .rcbr {
stmts << p.stmt(false)
}
p.next()
return ast.Block{
stmts: stmts
is_unsafe: true
}
}

View File

@ -13,7 +13,6 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | expr($precedence)')
}
// println('\n\nparser.expr()')
mut typ := table.void_type
mut node := ast.Expr{}
is_stmt_ident := p.is_stmt_ident
p.is_stmt_ident = false
@ -96,16 +95,18 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
node = p.if_expr(false)
}
.key_unsafe {
// unsafe {
p.next()
pos := p.tok.position()
assert !p.inside_unsafe
p.inside_unsafe = true
stmts := p.parse_block()
p.inside_unsafe = false
p.check(.lcbr)
node = ast.UnsafeExpr{
stmts: stmts
expr: p.expr(0)
pos: pos
}
p.check(.rcbr)
p.inside_unsafe = false
}
.key_lock, .key_rlock {
node = p.lock_expr()
@ -228,6 +229,11 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
p.error('expr(): bad token `$p.tok.kind.str()`')
}
}
return p.expr_with_left(node, precedence, is_stmt_ident)
}
pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bool) ast.Expr {
mut node := left
// Infix
for precedence < p.tok.precedence() {
if p.tok.kind == .dot {
@ -244,7 +250,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
// sum type as cast `x := SumType as Variant`
pos := p.tok.position()
p.next()
typ = p.parse_type()
typ := p.parse_type()
node = ast.AsCast{
expr: node
typ: typ

View File

@ -3,17 +3,9 @@ fn test_ptr_assign() {
mut p := &v[0]
unsafe {
(*p)++
}
unsafe {
p++
} // p now points to v[1]
unsafe {
p++ // p now points to v[1]
(*p) += 2
}
unsafe {
p += 2
} // p now points to v[3]
unsafe {
p += 2 // p now points to v[3]
*p = 31
}
assert v[0] == 6
@ -26,8 +18,7 @@ fn test_ptr_infix() {
v := 4
mut q := unsafe {&v - 1}
q = unsafe {q + 3}
_ := q
_ := v
assert q == unsafe {&v + 2}
}
struct S1 {
@ -44,3 +35,22 @@ fn test_funcs() {
}
_ = C.strerror(0) // [trusted] function prototype in builtin/cfns.c.v
}
fn test_if_expr_unsafe() {
i := 4
p := if true {
unsafe {&i}
}
else {unsafe {&i}}
assert *p == 4
}
fn test_unsafe_if_stmt() int {
i := 4
unsafe {
if true {
return (&i)[0]
}
}
return i
}