diff --git a/.release-notes/4027.md b/.release-notes/4027.md new file mode 100644 index 0000000000..19629cdfc1 --- /dev/null +++ b/.release-notes/4027.md @@ -0,0 +1,7 @@ +## Use symbol table from definition scope when looking up references from default method bodies + +Previously, symbols from default method bodies were only looked up in the local scope of the file into which they were included. This resulted in compilation errors if said symbol wasn't available in the local scope. + +Issues [#3737](https://github.com/ponylang/ponyc/issues/3737) and [#2150](https://github.com/ponylang/ponyc/issues/2150) were both examples of this problem. + +We've updated definition lookup to check if the enclosing method is provided by a trait or interface and if it is, to also check to definition scope for any needed symbols. diff --git a/src/libponyc/ast/ast.c b/src/libponyc/ast/ast.c index 60076b9a8d..db468cbdf5 100644 --- a/src/libponyc/ast/ast.c +++ b/src/libponyc/ast/ast.c @@ -1892,6 +1892,33 @@ void ast_extract_children(ast_t* parent, size_t child_count, } } +ast_t* ast_get_provided_symbol_definition(ast_t* ast, + const char* name, sym_status_t* status) +{ + // The definition isn't in the local scope. let's check to see if our + // parent has a provided method body from a trait or interface. If yes, + // then we will find our definition. + ast_t* def = NULL; + bool found = false; + + while(ast != NULL && !found) + { + if((ast_id(ast) == TK_FUN) || (ast_id(ast) == TK_BE)) + { + // Methods with defaults provided by a trait/interface store the ast + // of the provided body in the ast data. + ast_t* body_donor = (ast_t *)ast_data(ast); + if (body_donor != NULL) + def = ast_get(body_donor, name, status); + found = true; + } + + ast = ast_parent(ast); + } + + return def; +} + static void ast_signature_serialise_trace(pony_ctx_t* ctx, void* object) { ast_t* ast = (ast_t*)object; diff --git a/src/libponyc/ast/ast.h b/src/libponyc/ast/ast.h index 429ea8ac2d..f05084b6f5 100644 --- a/src/libponyc/ast/ast.h +++ b/src/libponyc/ast/ast.h @@ -213,6 +213,9 @@ void ast_extract_children(ast_t* parent, size_t child_count, children); \ } +ast_t* ast_get_provided_symbol_definition(ast_t* ast, + const char* name, sym_status_t* status); + pony_type_t* ast_signature_pony_type(); pony_type_t* ast_nominal_pkg_id_signature_pony_type(); diff --git a/src/libponyc/pass/names.c b/src/libponyc/pass/names.c index a91be727fa..b60287de07 100644 --- a/src/libponyc/pass/names.c +++ b/src/libponyc/pass/names.c @@ -319,6 +319,9 @@ bool names_nominal(pass_opt_t* opt, ast_t* scope, ast_t** astp, bool expr) } ast_t* def = ast_get(r_scope, name, NULL); + if(def == NULL) + def = ast_get_provided_symbol_definition(ast, name, NULL); + bool r = true; if(def == NULL) diff --git a/src/libponyc/pass/refer.c b/src/libponyc/pass/refer.c index bbc8475c66..1a683349b0 100644 --- a/src/libponyc/pass/refer.c +++ b/src/libponyc/pass/refer.c @@ -435,9 +435,10 @@ bool refer_reference(pass_opt_t* opt, ast_t** astp) return true; } - // Everything we reference must be in scope, so we can use ast_get for lookup. sym_status_t status; - ast_t* def = ast_get(ast, ast_name(ast_child(ast)), &status); + ast_t* def = ast_get(ast, name, &status); + if(def == NULL) + def = ast_get_provided_symbol_definition(ast, name, &status); // If nothing was found, we fail, but also try to suggest an alternate name. if(def == NULL) diff --git a/test/libponyc-run/regression-2150/bar.pony b/test/libponyc-run/regression-2150/bar.pony new file mode 100644 index 0000000000..6bb9a59022 --- /dev/null +++ b/test/libponyc-run/regression-2150/bar.pony @@ -0,0 +1,8 @@ +class Bar[T: Stringable #read] is Foo[T] + let _t: T + + new create(t: T) => + _t = t + + fun foo(): this->T => + _t diff --git a/test/libponyc-run/regression-2150/foo.pony b/test/libponyc-run/regression-2150/foo.pony new file mode 100644 index 0000000000..efc0996ac6 --- /dev/null +++ b/test/libponyc-run/regression-2150/foo.pony @@ -0,0 +1,7 @@ +use "debug" + +trait Foo[T: Stringable #read] + fun foo(): this->T + + fun bar() => + Debug.out(foo().string()) diff --git a/test/libponyc-run/regression-2150/main.pony b/test/libponyc-run/regression-2150/main.pony new file mode 100644 index 0000000000..859a7db0f3 --- /dev/null +++ b/test/libponyc-run/regression-2150/main.pony @@ -0,0 +1,4 @@ +actor Main + new create(env: Env) => + let b = Bar[I32](123) + b.bar() diff --git a/test/libponyc-run/regression-3737/lib/lib.pony b/test/libponyc-run/regression-3737/lib/lib.pony new file mode 100644 index 0000000000..ce19639a1d --- /dev/null +++ b/test/libponyc-run/regression-3737/lib/lib.pony @@ -0,0 +1,4 @@ +primitive LibPrimitive + +trait LibTrait + fun foo(): LibPrimitive => LibPrimitive diff --git a/test/libponyc-run/regression-3737/main.pony b/test/libponyc-run/regression-3737/main.pony new file mode 100644 index 0000000000..79edda24b8 --- /dev/null +++ b/test/libponyc-run/regression-3737/main.pony @@ -0,0 +1,5 @@ +use l = "./lib" + +actor Main is l.LibTrait + new create(env: Env) => + None