diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 3120167c8d..c26c4bc6a6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -13,7 +13,7 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | - StringInterLiteral | StringLiteral | StructInit | Type | TypeOf + StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompFor | CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | @@ -446,6 +446,12 @@ pub mut: left_as_name string // only used in x is SumType check } +pub struct UnsafeExpr { +pub: + stmts []Stmt + pos token.Position +} + pub struct LockExpr { pub: stmts []Stmt diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index d0b5ae2cc3..a10fcc006b 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -254,6 +254,9 @@ pub fn (x Expr) str() string { Likely { return '_likely_(${it.expr.str()})' } + UnsafeExpr { + return 'unsafe { $it.stmts.len stmts }' + } else { return '[unhandled expr type ${typeof(x)}]' } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 689e0142f8..d0deea50b6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2031,6 +2031,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.type_decl(node) } ast.UnsafeStmt { + assert !c.inside_unsafe c.inside_unsafe = true c.stmts(node.stmts) c.inside_unsafe = false @@ -2291,6 +2292,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { node.expr_type = c.expr(node.expr) return table.string_type } + ast.UnsafeExpr { + return c.unsafe_expr(mut node) + } ast.Likely { ltype := c.expr(node.expr) if !c.check_types(ltype, table.bool_type) { @@ -2630,6 +2634,34 @@ pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type { return table.void_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 +} + pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { mut expr_required := false if c.expected_type != table.void_type { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 8f39eaffe3..02d6b43830 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1051,6 +1051,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.expr(node.expr) f.write(')') } + ast.UnsafeExpr { + f.writeln('unsafe {') + f.stmts(it.stmts) + f.writeln('}') + } } } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 8363ff4e9e..e64fc5a947 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1837,6 +1837,10 @@ fn (mut g Gen) expr(node ast.Expr) { g.expr(node.expr) g.write(')') } + ast.UnsafeExpr { + es := node.stmts[0] as ast.ExprStmt + g.expr(es.expr) + } } } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index ad65157cfb..847256b875 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -633,6 +633,10 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.ComptimeCall { // TODO } + ast.UnsafeExpr { + es := it.stmts[0] as ast.ExprStmt + g.expr(es.expr) + } } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 77bf7656a5..b113bad532 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -572,6 +572,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { } .key_unsafe { p.next() + assert !p.inside_unsafe p.inside_unsafe = true stmts := p.parse_block() p.inside_unsafe = false diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index f1fdf8421b..50f2deedde 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -79,6 +79,18 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { .key_if { node = p.if_expr() } + .key_unsafe { + p.next() + pos := p.tok.position() + assert !p.inside_unsafe + p.inside_unsafe = true + stmts := p.parse_block() + p.inside_unsafe = false + node = ast.UnsafeExpr { + stmts: stmts + pos: pos + } + } .key_lock, .key_rlock { node = p.lock_expr() } diff --git a/vlib/v/tests/ptr_arithmetic_test.v b/vlib/v/tests/ptr_arithmetic_test.v index 94c1b30394..0d6bc29c25 100644 --- a/vlib/v/tests/ptr_arithmetic_test.v +++ b/vlib/v/tests/ptr_arithmetic_test.v @@ -6,11 +6,20 @@ fn test_ptr_arithmetic(){ p += 2 p = p - 1 } + assert p == unsafe {&v + 2} + p = unsafe { p + 1 } + assert p == unsafe {&v + 3} + r := unsafe { p++ } + assert r == unsafe {&v + 3} + assert p == unsafe {&v + 4} // byteptr, voidptr, charptr are handled differently - mut q := byteptr(1) + mut q := byteptr(10) unsafe { q -= 2 q = q + 1 } + assert q == byteptr(9) + s := unsafe { q - 1 } + assert s == byteptr(8) }