parser: support parsing unsafe (as first token) as UnsafeExpr (#6032)
parent
1c886ad067
commit
9958881cbe
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
t := c.expr(node.expr)
|
||||
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
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||
|
|
|
@ -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('}')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue