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)) } 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

View File

@ -491,7 +491,7 @@ pub mut:
pub struct UnsafeExpr { pub struct UnsafeExpr {
pub: pub:
stmts []Stmt expr Expr
pos token.Position pos token.Position
} }
@ -1082,6 +1082,15 @@ pub fn (expr Expr) is_lvalue() bool {
return false 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 { pub fn (stmt Stmt) position() token.Position {
match stmt { match stmt {
AssertStmt { return stmt.pos } AssertStmt { return stmt.pos }

View File

@ -264,7 +264,7 @@ pub fn (x Expr) str() string {
return '_likely_(${it.expr.str()})' return '_likely_(${it.expr.str()})'
} }
UnsafeExpr { UnsafeExpr {
return 'unsafe { $it.stmts.len stmts }' return 'unsafe { $it.expr }'
} }
else {} 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 { 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 assert !c.inside_unsafe
c.inside_unsafe = true c.inside_unsafe = true
defer { t := c.expr(node.expr)
c.inside_unsafe = false c.inside_unsafe = false
} return t
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
} }
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { 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 { ast.UnsafeExpr {
f.write('unsafe {') f.write('unsafe {')
es := node.stmts[0] as ast.ExprStmt f.expr(node.expr)
f.expr(es.expr)
f.write('}') f.write('}')
} }
} }

View File

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

View File

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

View File

@ -623,27 +623,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
} }
} }
.key_unsafe { .key_unsafe {
if p.peek_tok.kind == .lcbr { return p.unsafe_stmt()
// 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
}
} }
.hash { .hash {
return p.hash() return p.hash()
@ -1988,3 +1968,55 @@ pub fn (mut p Parser) mark_var_as_used(varname string) bool {
} }
return false 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)') 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()') // println('\n\nparser.expr()')
mut typ := table.void_type
mut node := ast.Expr{} mut node := ast.Expr{}
is_stmt_ident := p.is_stmt_ident is_stmt_ident := p.is_stmt_ident
p.is_stmt_ident = false p.is_stmt_ident = false
@ -96,16 +95,18 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
node = p.if_expr(false) node = p.if_expr(false)
} }
.key_unsafe { .key_unsafe {
// unsafe {
p.next() p.next()
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() p.check(.lcbr)
p.inside_unsafe = false
node = ast.UnsafeExpr{ node = ast.UnsafeExpr{
stmts: stmts expr: p.expr(0)
pos: pos pos: pos
} }
p.check(.rcbr)
p.inside_unsafe = false
} }
.key_lock, .key_rlock { .key_lock, .key_rlock {
node = p.lock_expr() 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()`') 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 // Infix
for precedence < p.tok.precedence() { for precedence < p.tok.precedence() {
if p.tok.kind == .dot { 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` // sum type as cast `x := SumType as Variant`
pos := p.tok.position() pos := p.tok.position()
p.next() p.next()
typ = p.parse_type() typ := p.parse_type()
node = ast.AsCast{ node = ast.AsCast{
expr: node expr: node
typ: typ typ: typ

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
@ -26,8 +18,7 @@ fn test_ptr_infix() {
v := 4 v := 4
mut q := unsafe {&v - 1} mut q := unsafe {&v - 1}
q = unsafe {q + 3} q = unsafe {q + 3}
_ := q assert q == unsafe {&v + 2}
_ := v
} }
struct S1 { struct S1 {
@ -44,3 +35,22 @@ fn test_funcs() {
} }
_ = C.strerror(0) // [trusted] function prototype in builtin/cfns.c.v _ = 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
}