parser/cgen: multiple attributes & better errors (closes #5334)

pull/5389/head
joe-conigliaro 2020-06-15 22:59:09 +10:00
parent 91df872c36
commit 0cd9066f44
No known key found for this signature in database
GPG Key ID: C12F7136C08206F1
17 changed files with 143 additions and 99 deletions

View File

@ -162,7 +162,7 @@ pub:
pub_mut_pos int // pub mut: pub_mut_pos int // pub mut:
language table.Language language table.Language
is_union bool is_union bool
attr string attrs []string
} }
pub struct InterfaceDecl { pub struct InterfaceDecl {

View File

@ -1,5 +0,0 @@
vlib/v/checker/tests/fn_attributes_empty_err.v:1:1: error: attributes cannot be empty
1 | [] fn tt() {
| ~~
2 | println('text')
3 | }

View File

@ -1,4 +0,0 @@
vlib/v/checker/tests/multiple_fn_attributes.v:1:1: error: multiple attributes detected
1 | [inline;deprecated]
| ~~~~~~~~~~~~~~~~~~~
2 | fn foo(name string) string {}

View File

@ -1,2 +0,0 @@
[inline;deprecated]
fn foo(name string) string {}

View File

@ -98,7 +98,7 @@ mut:
is_json_fn bool // inside json.encode() is_json_fn bool // inside json.encode()
json_types []string // to avoid json gen duplicates json_types []string // to avoid json gen duplicates
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
attr string attrs []string // attributes before next decl stmt
is_builtin_mod bool is_builtin_mod bool
hotcode_fn_names []string hotcode_fn_names []string
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last* fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
@ -573,6 +573,10 @@ fn (mut g Gen) stmts(stmts []ast.Stmt) {
if g.inside_ternary > 0 && i < stmts.len - 1 { if g.inside_ternary > 0 && i < stmts.len - 1 {
g.write(',') g.write(',')
} }
// clear attrs on next non Attr stmt
if stmt !is ast.Attr {
g.attrs = []
}
} }
g.indent-- g.indent--
if g.inside_ternary > 0 { if g.inside_ternary > 0 {
@ -593,7 +597,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.gen_assign_stmt(it) g.gen_assign_stmt(it)
} }
ast.Attr { ast.Attr {
g.attr = it.name g.attrs << it.name
g.writeln('// Attr: [$it.name]') g.writeln('// Attr: [$it.name]')
} }
ast.Block { ast.Block {
@ -685,8 +689,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
if it.language != .c { if it.language != .c {
g.writeln('') g.writeln('')
} }
// g.attr has to be reset after each function
g.attr = ''
} }
ast.ForCStmt { ast.ForCStmt {
g.write('for (') g.write('for (')

View File

@ -38,9 +38,9 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
return return
} }
fn_start_pos := g.out.len fn_start_pos := g.out.len
msvc_attrs := g.write_fn_attr() msvc_attrs := g.write_fn_attrs()
// Live // Live
is_livefn := g.attr == 'live' is_livefn := 'live' in g.attrs
is_livemain := g.pref.is_livemain && is_livefn is_livemain := g.pref.is_livemain && is_livefn
is_liveshared := g.pref.is_liveshared && is_livefn is_liveshared := g.pref.is_liveshared && is_livefn
is_livemode := g.pref.is_livemain || g.pref.is_liveshared is_livemode := g.pref.is_livemain || g.pref.is_liveshared
@ -703,78 +703,79 @@ fn (g &Gen) fileis(s string) bool {
return g.file.path.contains(s) return g.file.path.contains(s)
} }
fn (mut g Gen) write_fn_attr() string{ fn (mut g Gen) write_fn_attrs() string{
mut msvc_attrs := '' mut msvc_attrs := ''
match g.attr { for attr in g.attrs {
'inline' { match attr {
g.write('inline ') 'inline' {
} g.write('inline ')
// since these are supported by GCC, clang and MSVC, we can consider them officially supported. }
'no_inline' { // since these are supported by GCC, clang and MSVC, we can consider them officially supported.
g.write('__NOINLINE ') 'no_inline' {
} g.write('__NOINLINE ')
'irq_handler' { }
g.write('__IRQHANDLER ') 'irq_handler' {
} g.write('__IRQHANDLER ')
}
// GCC/clang attributes // GCC/clang attributes
// prefixed by _ to indicate they're for advanced users only and not really supported by V. // prefixed by _ to indicate they're for advanced users only and not really supported by V.
// source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
// The cold attribute on functions is used to inform the compiler that the function is unlikely // The cold attribute on functions is used to inform the compiler that the function is unlikely
// to be executed. The function is optimized for size rather than speed and on many targets it // to be executed. The function is optimized for size rather than speed and on many targets it
// is placed into a special subsection of the text section so all cold functions appear close // is placed into a special subsection of the text section so all cold functions appear close
// together, improving code locality of non-cold parts of program. // together, improving code locality of non-cold parts of program.
'_cold' { '_cold' {
g.write('__attribute__((cold)) ') g.write('__attribute__((cold)) ')
} }
// The constructor attribute causes the function to be called automatically before execution // The constructor attribute causes the function to be called automatically before execution
// enters main (). // enters main ().
'_constructor' { '_constructor' {
g.write('__attribute__((constructor)) ') g.write('__attribute__((constructor)) ')
} }
// The destructor attribute causes the function to be called automatically after main () // The destructor attribute causes the function to be called automatically after main ()
// completes or exit () is called. // completes or exit () is called.
'_destructor' { '_destructor' {
g.write('__attribute__((destructor)) ') g.write('__attribute__((destructor)) ')
} }
// Generally, inlining into a function is limited. For a function marked with this attribute, // Generally, inlining into a function is limited. For a function marked with this attribute,
// every call inside this function is inlined, if possible. // every call inside this function is inlined, if possible.
'_flatten' { '_flatten' {
g.write('__attribute__((flatten)) ') g.write('__attribute__((flatten)) ')
} }
// The hot attribute on a function is used to inform the compiler that the function is a hot // The hot attribute on a function is used to inform the compiler that the function is a hot
// spot of the compiled program. // spot of the compiled program.
'_hot' { '_hot' {
g.write('__attribute__((hot)) ') g.write('__attribute__((hot)) ')
} }
// This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by
// the function cannot alias any other pointer valid when the function returns, and moreover no // the function cannot alias any other pointer valid when the function returns, and moreover no
// pointers to valid objects occur in any storage addressed by P. // pointers to valid objects occur in any storage addressed by P.
'_malloc' { '_malloc' {
g.write('__attribute__((malloc)) ') g.write('__attribute__((malloc)) ')
} }
// Calls to functions whose return value is not affected by changes to the observable state // Calls to functions whose return value is not affected by changes to the observable state
// of the program and that have no observable effects on such state other than to return a // of the program and that have no observable effects on such state other than to return a
// value may lend themselves to optimizations such as common subexpression elimination. // value may lend themselves to optimizations such as common subexpression elimination.
// Declaring such functions with the const attribute allows GCC to avoid emitting some calls in // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in
// repeated invocations of the function with the same argument values. // repeated invocations of the function with the same argument values.
'_pure' { '_pure' {
g.write('__attribute__((const)) ') g.write('__attribute__((const)) ')
} }
// windows attributes (msvc/mingw) // windows attributes (msvc/mingw)
// prefixed by windows to indicate they're for advanced users only and not really supported by V. // prefixed by windows to indicate they're for advanced users only and not really supported by V.
'windows_stdcall' { 'windows_stdcall' {
msvc_attrs += '__stdcall ' msvc_attrs += '__stdcall '
} }
else { else {
// nothing but keep V happy // nothing but keep V happy
}
} }
} }
return msvc_attrs return msvc_attrs
}
}

View File

@ -12,7 +12,7 @@ fn (mut g Gen) profile_fn(fn_name string, is_main bool){
g.writeln('\tatexit(vprint_profile_stats);') g.writeln('\tatexit(vprint_profile_stats);')
g.writeln('') g.writeln('')
} }
if g.pref.profile_no_inline && g.attr == 'inline' { if g.pref.profile_no_inline && 'inline' in g.attrs {
g.defer_profile_code = '' g.defer_profile_code = ''
return return
} }

View File

@ -119,7 +119,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
fn (mut p Parser) fn_decl() ast.FnDecl { fn (mut p Parser) fn_decl() ast.FnDecl {
p.top_level_statement_start() p.top_level_statement_start()
start_pos := p.tok.position() start_pos := p.tok.position()
is_deprecated := p.attr == 'deprecated' is_deprecated := 'deprecated' in p.attrs
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
p.next() p.next()
@ -273,8 +273,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
stmts = p.parse_block_no_scope(true) stmts = p.parse_block_no_scope(true)
} }
p.close_scope() p.close_scope()
p.attr = ''
p.attr_ctdefine = ''
return ast.FnDecl{ return ast.FnDecl{
name: name name: name
mod: p.mod mod: p.mod

View File

@ -36,7 +36,7 @@ mut:
pref &pref.Preferences pref &pref.Preferences
builtin_mod bool // are we in the `builtin` module? builtin_mod bool // are we in the `builtin` module?
mod string // current module name mod string // current module name
attr string attrs []string // attributes before next decl stmt
attr_ctdefine string attr_ctdefine string
expr_mod string // for constructing full type names in parse_type() expr_mod string // for constructing full type names in parse_type()
scope &ast.Scope scope &ast.Scope
@ -154,7 +154,13 @@ fn (mut p Parser) parse() ast.File {
break break
} }
// println('stmt at ' + p.tok.str()) // println('stmt at ' + p.tok.str())
stmts << p.top_stmt() stmt := p.top_stmt()
// clear the attribtes at the end of next non Attr top level stmt
if stmt !is ast.Attr {
p.attrs = []
p.attr_ctdefine = ''
}
stmts << stmt
} }
// println('nr stmts = $stmts.len') // println('nr stmts = $stmts.len')
// println(stmts[0]) // println(stmts[0])
@ -409,10 +415,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
.lsbr { .lsbr {
start_pos := p.tok.position() start_pos := p.tok.position()
attrs := p.attributes() attrs := p.attributes()
if attrs.len > 1 { if attrs.len == 0 {
end_pos := p.tok.position()
p.error_with_pos('multiple attributes detected', start_pos.extend(end_pos))
} else if attrs.len == 0 {
end_pos := p.tok.position() end_pos := p.tok.position()
p.error_with_pos('attributes cannot be empty', start_pos.extend(end_pos)) p.error_with_pos('attributes cannot be empty', start_pos.extend(end_pos))
} }
@ -634,6 +637,7 @@ fn (mut p Parser) attributes() []ast.Attr {
} }
fn (mut p Parser) parse_attr() ast.Attr { fn (mut p Parser) parse_attr() ast.Attr {
start_pos := p.prev_tok.position()
mut is_if_attr := false mut is_if_attr := false
if p.tok.kind == .key_if { if p.tok.kind == .key_if {
p.next() p.next()
@ -650,7 +654,10 @@ fn (mut p Parser) parse_attr() ast.Attr {
p.next() p.next()
} }
} }
p.attr = name if name in p.attrs {
p.error_with_pos('duplicate attribute `$name`', start_pos.extend(p.tok.position()))
}
p.attrs << name
if is_if_attr { if is_if_attr {
p.attr_ctdefine = name p.attr_ctdefine = name
} }
@ -1460,8 +1467,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
} }
p.top_level_statement_end() p.top_level_statement_end()
p.check(.rcbr) p.check(.rcbr)
attr := p.attr is_flag := 'flag' in p.attrs
is_flag := attr == 'flag'
if is_flag { if is_flag {
if fields.len > 32 { if fields.len > 32 {
p.error('when an enum is used as bit field, it must have a max of 32 fields') p.error('when an enum is used as bit field, it must have a max of 32 fields')

View File

@ -32,7 +32,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
p.next() // C || JS p.next() // C || JS
p.next() // . p.next() // .
} }
is_typedef := p.attr == 'typedef' is_typedef := 'typedef' in p.attrs
no_body := p.peek_tok.kind != .lcbr no_body := p.peek_tok.kind != .lcbr
if language == .v && no_body { if language == .v && no_body {
p.error('`$p.tok.lit` lacks body') p.error('`$p.tok.lit` lacks body')
@ -180,7 +180,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
fields: fields fields: fields
is_typedef: is_typedef is_typedef: is_typedef
is_union: is_union is_union: is_union
is_ref_only: p.attr == 'ref_only' is_ref_only: 'ref_only' in p.attrs
} }
mod: p.mod mod: p.mod
is_public: is_pub is_public: is_pub
@ -208,7 +208,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
pub_mut_pos: pub_mut_pos pub_mut_pos: pub_mut_pos
language: language language: language
is_union: is_union is_union: is_union
attr: p.attr attrs: p.attrs
} }
} }

View File

@ -0,0 +1,6 @@
vlib/v/parser/tests/fn_attributes_duplicate_multiple.v:2:1: error: duplicate attribute `inline`
1 | [inline]
2 | [inline]
| ~~~~~~~~
3 | fn foo() {}
4 |

View File

@ -0,0 +1,7 @@
[inline]
[inline]
fn foo() {}
fn main() {
foo()
}

View File

@ -0,0 +1,5 @@
vlib/v/parser/tests/fn_attributes_duplicate_single.v:1:8: error: duplicate attribute `inline`
1 | [inline; inline]
| ~~~~~~~~~
2 | fn foo() {}
3 |

View File

@ -0,0 +1,6 @@
[inline; inline]
fn foo() {}
fn main() {
foo()
}

View File

@ -0,0 +1,5 @@
vlib/v/parser/tests/fn_attributes_empty_err.v:1:1: error: attributes cannot be empty
1 | [] fn tt() {
| ~~
2 | println('text')
3 | }

View File

@ -22,6 +22,14 @@ pub enum PubEnumAttrTest {
two two
} }
[attrone]
[attrtwo]
pub enum EnumMultiAttrTest {
one
two
}
[testing] [testing]
fn test_fn_attribute() { fn test_fn_attribute() {
assert true assert true
@ -31,3 +39,14 @@ fn test_fn_attribute() {
pub fn test_pub_fn_attribute() { pub fn test_pub_fn_attribute() {
assert true assert true
} }
[attrone]
[attrtwo]
fn test_fn_multi_attribute() {
assert true
}
[attrone; attrtwo]
fn test_fn_multi_attribute_single() {
assert true
}