diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0fc6104eab..9ea39054b2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -124,6 +124,7 @@ mut: inside_println_arg bool inside_decl_rhs bool inside_if_guard bool // true inside the guard condition of `if x := opt() {}` + vweb_comptime_call_pos int // needed for correctly checking use before decl for vweb templates } pub fn new_checker(table &ast.Table, pref &pref.Preferences) &Checker { @@ -3104,9 +3105,15 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { return obj.typ } ast.Var { - // incase var was not marked as used yet (vweb tmpl) - // obj.is_used = true - if node.pos.pos < obj.pos.pos { + // inside vweb tmpl ident positions are meaningless, use the position of the comptime call. + // if the variable is declared before the comptime call then we can assume all is well. + // `node.name !in node.scope.objects` checks it's an inherited var (not defined in the tmpl). + node_pos := if c.pref.is_vweb && node.name !in node.scope.objects { + c.vweb_comptime_call_pos + } else { + node.pos.pos + } + if node_pos < obj.pos.pos { c.error('undefined variable `$node.name` (used before declaration)', node.pos) } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 4dd5ec3af7..aff684ef00 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -36,28 +36,8 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { is_vweb: true } mut c2 := new_checker(c.table, pref2) + c2.vweb_comptime_call_pos = node.pos.pos c2.check(node.vweb_tmpl) - mut caller_scope := c.fn_scope.innermost(node.pos.pos) - mut i := 0 // tmp counter var for skipping first three tmpl vars - for k, _ in c2.file.scope.children[0].objects { - if i < 2 { - // Skip first three because they are tmpl vars see vlib/vweb/tmpl/tmpl.v - i++ - continue - } - tmpl_obj := unsafe { c2.file.scope.children[0].objects[k] } - if tmpl_obj is ast.Var { - if mut caller_var := caller_scope.find_var(tmpl_obj.name) { - // var is used in the tmpl so mark it as used in the caller - caller_var.is_used = true - // update props from the caller scope var to the tmpl scope var - c2.file.scope.children[0].objects[k] = ast.Var{ - ...(*caller_var) - pos: tmpl_obj.pos - } - } - } - } c.warnings << c2.warnings c.errors << c2.errors c.notices << c2.notices diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c77803429a..7cc472feb7 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3697,10 +3697,8 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('${name}.val') return } - // TODO: investigate why node.obj is pointing to outdated ScopeObject? - // v := node.obj - // if v is ast.Var { - if v := node.scope.find_var(node.name) { + v := node.obj + if v is ast.Var { is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign) if is_auto_heap { g.write('(*(') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 6d07664354..76752823e8 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -245,10 +245,6 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { println('$path:${i + 1}: $line') } } - mut scope := &ast.Scope{ - start_pos: 0 - parent: p.table.global_scope - } $if trace_comptime ? { println('') println('>>> template for $path:') @@ -256,25 +252,11 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { println('>>> end of template END') println('') } - mut file := parse_comptime(v_code, p.table, p.pref, scope) + // the tmpl inherits all parent scopes. previous functionality was just to + // inherit the scope from which the comptime call was made and no parents. + // this is much simpler and allws access to globals. can be changed if needed. + mut file := parse_comptime(tmpl_path, v_code, p.table, p.pref, p.scope) file.path = tmpl_path - // copy vars from current fn scope into vweb_tmpl scope - for mut stmt in file.stmts { - if mut stmt is ast.FnDecl { - if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { - for _, mut obj in p.scope.objects { - if mut obj is ast.Var { - stmt.scope.register(ast.Var{ - ...obj - is_used: true - pos: stmt.body_pos - }) - } - } - break - } - } - } return ast.ComptimeCall{ scope: 0 is_vweb: true diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index f19384aca8..91afa31779 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -115,11 +115,12 @@ pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt { return p.stmt(false) } -pub fn parse_comptime(text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File { +pub fn parse_comptime(tmpl_path string, text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File { $if trace_parse_comptime ? { eprintln('> ${@MOD}.${@FN} text: $text') } mut p := Parser{ + file_name: tmpl_path scanner: scanner.new_scanner(text, .skip_comments, pref) table: table pref: pref diff --git a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl new file mode 100644 index 0000000000..742feb74b3 --- /dev/null +++ b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl @@ -0,0 +1,2 @@ +$a.name +$b \ No newline at end of file diff --git a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v new file mode 100644 index 0000000000..18db0e9a2b --- /dev/null +++ b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v @@ -0,0 +1,11 @@ +[heap] +struct MyHeapStruct { + name string +} + +// make sure dereferencing of heap stucts works in selector expr (in tmpl), +fn test_heap_struct_dereferencing_in_selector_expr() { + a := MyHeapStruct{name: 'my_heap_struct_a'} + b := 2 + $tmpl('comptime_call_tmpl_variable_scope_test.tpl') +}