From e16442df36055f66eb3f3b50c495604d0d04001a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 17:21:29 +0200 Subject: [PATCH 01/14] Allow `use` of non-module declarations --- include/artic/ast.h | 14 ++++++++++++-- src/ast.cpp | 10 ++++++++++ src/check.cpp | 15 +++++++-------- src/emit.cpp | 4 +++- test/CMakeLists.txt | 4 +++- test/simple/{use.art => use1.art} | 0 test/simple/use2.art | 14 ++++++++++++++ test/simple/use3.art | 8 ++++++++ 8 files changed, 57 insertions(+), 12 deletions(-) rename test/simple/{use.art => use1.art} (100%) create mode 100644 test/simple/use2.art create mode 100644 test/simple/use3.art diff --git a/include/artic/ast.h b/include/artic/ast.h index 0b1739fa..1641678c 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "artic/loc.h" #include "artic/log.h" @@ -198,9 +199,9 @@ struct Path : public Node { : Node(loc), elems(std::move(elems)) {} - const artic::Type* infer(TypeChecker&, bool, Ptr* = nullptr); + const artic::Type* infer(TypeChecker&, std::optional, Ptr* = nullptr); const artic::Type* infer(TypeChecker& checker) override { - return infer(checker, false, nullptr); + return infer(checker, std::nullopt, nullptr); } const thorin::Def* emit(Emitter&) const override; @@ -1207,6 +1208,8 @@ struct NamedDecl : public Decl { NamedDecl(const Loc& loc, Identifier&& id) : Decl(loc), id(std::move(id)) {} + + virtual bool is_value(); }; /// Value declaration associated with an identifier. @@ -1214,6 +1217,8 @@ struct ValueDecl : public NamedDecl { ValueDecl(const Loc& loc, Identifier&& id) : NamedDecl(loc, std::move(id)) {} + + bool is_value() override; }; /// Datatype declaration with a constructor associated with an identifier. @@ -1529,6 +1534,9 @@ struct ModDecl : public NamedDecl { struct UseDecl : public NamedDecl { Path path; + // Set during type-checking + bool is_value_ = false; + UseDecl(const Loc& loc, Path&& path, Identifier&& id) : NamedDecl(loc, std::move(id)), path(std::move(path)) {} @@ -1539,6 +1547,8 @@ struct UseDecl : public NamedDecl { void bind(NameBinder&) override; void resolve_summons(Summoner&) override {}; void print(Printer&) const override; + + bool is_value() override; }; /// Incorrect declaration, coming from parsing. diff --git a/src/ast.cpp b/src/ast.cpp index f11eb046..992eda87 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -607,6 +607,16 @@ bool AsmExpr::has_side_effect() const { return !outs.empty() || std::find(opts.begin(), opts.end(), "volatile") != opts.end(); } +// Decls --------------------------------------------------------------------------- + +bool NamedDecl::is_value() { return false; } + +bool ValueDecl::is_value() { return true; } + +bool UseDecl::is_value() { + return is_value_; +} + // Patterns ------------------------------------------------------------------------ void Ptrn::collect_bound_ptrns(std::vector&) const {} diff --git a/src/check.cpp b/src/check.cpp index ccbacbd5..09abb9ed 100644 --- a/src/check.cpp +++ b/src/check.cpp @@ -625,14 +625,14 @@ const artic::Type* Ptrn::check(TypeChecker& checker, const artic::Type* expected // Path ---------------------------------------------------------------------------- -const artic::Type* Path::infer(TypeChecker& checker, bool value_expected, Ptr* arg) { +const artic::Type* Path::infer(TypeChecker& checker, std::optional value_expected, Ptr* arg) { if (!start_decl) return checker.type_table.type_error(); type = elems[0].is_super() ? checker.type_table.mod_type(*start_decl->as()) : checker.infer(*start_decl); - is_value = elems.size() == 1 && start_decl->isa(); + is_value = elems.size() == 1 && start_decl->is_value(); is_ctor = start_decl->isa(); // Inspect every element of the path @@ -673,7 +673,7 @@ const artic::Type* Path::infer(TypeChecker& checker, bool value_expected, Ptr(type); - is_ctor && value_expected && struct_type && struct_type->is_tuple_like()) { + is_ctor && (*value_expected && value_expected) && struct_type && struct_type->is_tuple_like()) { if (struct_type->member_count() > 0) { SmallArray tuple_args(struct_type->member_count()); for (size_t i = 0, n = struct_type->member_count(); i < n; ++i) @@ -723,15 +723,15 @@ const artic::Type* Path::infer(TypeChecker& checker, bool value_expected, Ptr() ? checker.type_table.mod_type(*member.as()) : checker.infer(mod_type->member(*index)); - is_value = member.isa(); + is_value = member.is_value(); is_ctor = member.isa(); } else return checker.type_expected(elem.loc, type, "module or enum"); } } - if (is_value != value_expected) { - checker.error(loc, "{} expected, but got '{}'", value_expected ? "value" : "type", *this); + if (value_expected && is_value != *value_expected) { + checker.error(loc, "{} expected, but got '{}'", *value_expected ? "value" : "type", *this); return checker.type_table.type_error(); } return type; @@ -1732,9 +1732,8 @@ const artic::Type* UseDecl::infer(TypeChecker& checker) { if (!checker.enter_decl(this)) return checker.type_table.type_error(); auto path_type = checker.infer(path); + is_value_ = path.is_value; checker.exit_decl(this); - if (!path_type->isa()) - return checker.type_expected(path.loc, path_type, "module type"); return path_type; } diff --git a/src/emit.cpp b/src/emit.cpp index 2e49d83b..986d71b4 100644 --- a/src/emit.cpp +++ b/src/emit.cpp @@ -1760,7 +1760,9 @@ const thorin::Def* ModDecl::emit(Emitter& emitter) const { return nullptr; } -const thorin::Def* UseDecl::emit(Emitter&) const { +const thorin::Def* UseDecl::emit(Emitter& emitter) const { + if (path.is_value) + return path.emit(emitter); return nullptr; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a1e3f70e..c4daddb9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -62,7 +62,9 @@ add_test(NAME simple_ops COMMAND artic --print-ast ${CMAKE_CURRENT_SOURC add_test(NAME simple_mod1 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/mod1.art) add_test(NAME simple_mod2 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/mod2.art) add_test(NAME simple_mod3 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/mod3.art) -add_test(NAME simple_use COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use.art) +add_test(NAME simple_use1 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use1.art) +add_test(NAME simple_use2 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use2.art) +add_test(NAME simple_use3 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use3.art) add_test(NAME simple_poly_fn1 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/poly_fn1.art) add_test(NAME simple_poly_fn2 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/poly_fn2.art) add_test(NAME simple_nested_fns COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/nested_fns.art) diff --git a/test/simple/use.art b/test/simple/use1.art similarity index 100% rename from test/simple/use.art rename to test/simple/use1.art diff --git a/test/simple/use2.art b/test/simple/use2.art new file mode 100644 index 00000000..80353a57 --- /dev/null +++ b/test/simple/use2.art @@ -0,0 +1,14 @@ +mod blob { + struct Blob { x: i32 } + + fn g(x: i32, y: i32) = x + y; +} + +use blob::Blob; +use blob::g; + +fn main(a: i32) -> Blob { + let x = g(a, -a); + let b = Blob { x = x }; + b +} diff --git a/test/simple/use3.art b/test/simple/use3.art new file mode 100644 index 00000000..6848afea --- /dev/null +++ b/test/simple/use3.art @@ -0,0 +1,8 @@ +mod foo { + static x: i32 = 42; + + fn f() = super::bar::y; +} +mod bar { + use super::foo::x as y; +} From a82a3fce271262678ef2a735892ec96310a5310d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 17:28:18 +0200 Subject: [PATCH 02/14] remove 'allow_type' from parse_path as it's always true --- include/artic/parser.h | 4 ++-- src/emit.cpp | 1 + src/parser.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/artic/parser.h b/include/artic/parser.h index 15b248f2..e60de01b 100644 --- a/include/artic/parser.h +++ b/include/artic/parser.h @@ -101,8 +101,8 @@ class Parser : public Logger { Ptr parse_attr_list(); Ptr parse_attr(); - ast::Path parse_path(ast::Identifier&&, bool); - ast::Path parse_path(bool allow_types = true) { return parse_path(parse_path_elem(), allow_types); } + ast::Path parse_path(ast::Identifier&&); + ast::Path parse_path() { return parse_path(parse_path_elem()); } ast::Identifier parse_path_elem(); ast::Identifier parse_id(); ast::AsmExpr::Constr parse_constr(); diff --git a/src/emit.cpp b/src/emit.cpp index 986d71b4..1080d350 100644 --- a/src/emit.cpp +++ b/src/emit.cpp @@ -1181,6 +1181,7 @@ const thorin::Def* TypedExpr::emit(Emitter& emitter) const { } const thorin::Def* PathExpr::emit(Emitter& emitter) const { + assert(emitter.emit(path)); return emitter.emit(path); } diff --git a/src/parser.cpp b/src/parser.cpp index 2f3634e2..5e4af15a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -299,7 +299,7 @@ Ptr Parser::parse_ptrn(bool allow_types, bool allow_implicits) { ahead().tag() == Token::LParen || ahead().tag() == Token::LBrace || (allow_types && ahead().tag() != Token::Colon && ahead().tag() != Token::As)) { - auto path = parse_path(std::move(id), true); + auto path = parse_path(std::move(id)); if (ahead().tag() == Token::LBrace) ptrn = parse_record_ptrn(std::move(path)); else if (allow_types) { @@ -486,7 +486,7 @@ Ptr Parser::parse_typed_expr(Ptr&& expr) { } Ptr Parser::parse_path_expr() { - auto path = parse_path(true); + auto path = parse_path(); return make_ptr(std::move(path)); } @@ -1171,7 +1171,7 @@ Ptr Parser::parse_attr() { } } -ast::Path Parser::parse_path(ast::Identifier&& id, bool allow_types) { +ast::Path Parser::parse_path(ast::Identifier&& id) { Tracker tracker(this, id.loc); std::vector elems; @@ -1179,7 +1179,7 @@ ast::Path Parser::parse_path(ast::Identifier&& id, bool allow_types) { Tracker elem_tracker(this, id.loc); PtrVector args; // Do not accept type arguments on `super` - if (allow_types && id.name != "super" && accept(Token::LBracket)) { + if (id.name != "super" && accept(Token::LBracket)) { parse_list(Token::RBracket, Token::Comma, [&] { args.emplace_back(parse_type()); }); From 4f15a45962a2153a3aebcd4e1a7e1a4c0dde7b3f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:44:55 +0200 Subject: [PATCH 03/14] remove dead field in UseDecl --- include/artic/ast.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/artic/ast.h b/include/artic/ast.h index 1641678c..7eeb2e9b 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -1504,8 +1504,6 @@ struct ModDecl : public NamedDecl { PtrVector decls; ModDecl* super = nullptr; - std::vector members; - /// Constructor for the implicitly defined global module. /// When using this constructor, the user is responsible for calling /// `set_super()` once the declarations have been added to the module. From 48eacc8af31ecb3e03eaae37cfd2a9b59809b0e6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:45:19 +0200 Subject: [PATCH 04/14] add find_member helper method on ModDecl --- include/artic/ast.h | 1 + src/bind.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/artic/ast.h b/include/artic/ast.h index 7eeb2e9b..84adbd72 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -1519,6 +1519,7 @@ struct ModDecl : public NamedDecl { } void set_super(); + std::optional find_member(const std::string_view& name) const; const thorin::Def* emit(Emitter&) const override; const artic::Type* infer(TypeChecker&) override; diff --git a/src/bind.cpp b/src/bind.cpp index e094c608..b38f8271 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -524,6 +524,15 @@ void ModDecl::bind(NameBinder& binder) { binder.cur_mod = old_mod; } +std::optional ModDecl::find_member(const std::string_view& name) const { + for (const auto& decl : decls) { + if (auto named = decl->isa()) + if (named->id.name == name) + return std::make_optional(named); + } + return std::nullopt; +} + void UseDecl::bind_head(NameBinder& binder) { if (id.name != "") binder.insert_symbol(*this); From 232a92f37a87200aeaebcd25c4910a680dea86ee Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:45:47 +0200 Subject: [PATCH 05/14] parser: added wildcard support to Path --- include/artic/parser.h | 6 +++--- src/parser.cpp | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/artic/parser.h b/include/artic/parser.h index e60de01b..fbecaeab 100644 --- a/include/artic/parser.h +++ b/include/artic/parser.h @@ -101,9 +101,9 @@ class Parser : public Logger { Ptr parse_attr_list(); Ptr parse_attr(); - ast::Path parse_path(ast::Identifier&&); - ast::Path parse_path() { return parse_path(parse_path_elem()); } - ast::Identifier parse_path_elem(); + ast::Path parse_path(ast::Identifier&&, bool allow_wildcard); + ast::Path parse_path(bool allow_wildcard = false) { return parse_path(parse_path_elem(), allow_wildcard); } + ast::Identifier parse_path_elem(bool allow_wildcard = false); ast::Identifier parse_id(); ast::AsmExpr::Constr parse_constr(); Literal parse_lit(); diff --git a/src/parser.cpp b/src/parser.cpp index 5e4af15a..d09ff24d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -258,7 +258,7 @@ Ptr Parser::parse_mod_decl() { Ptr Parser::parse_use_decl() { Tracker tracker(this); eat(Token::Use); - auto path = parse_path(); + auto path = parse_path(true); ast::Identifier id; if (accept(Token::As)) id = parse_id(); @@ -299,7 +299,7 @@ Ptr Parser::parse_ptrn(bool allow_types, bool allow_implicits) { ahead().tag() == Token::LParen || ahead().tag() == Token::LBrace || (allow_types && ahead().tag() != Token::Colon && ahead().tag() != Token::As)) { - auto path = parse_path(std::move(id)); + auto path = parse_path(std::move(id), false); if (ahead().tag() == Token::LBrace) ptrn = parse_record_ptrn(std::move(path)); else if (allow_types) { @@ -1171,7 +1171,7 @@ Ptr Parser::parse_attr() { } } -ast::Path Parser::parse_path(ast::Identifier&& id) { +ast::Path Parser::parse_path(ast::Identifier&& id, bool allow_wildcard) { Tracker tracker(this, id.loc); std::vector elems; @@ -1185,16 +1185,20 @@ ast::Path Parser::parse_path(ast::Identifier&& id) { }); } elems.emplace_back(elem_tracker(), std::move(id), std::move(args)); + if (id.name == "*") + break; if (!accept(Token::DblColon)) break; - id = parse_path_elem(); + id = parse_path_elem(allow_wildcard); } while (true) ; return ast::Path(tracker(), std::move(elems)); } -ast::Identifier Parser::parse_path_elem() { +ast::Identifier Parser::parse_path_elem(bool allow_wildcard) { auto prev_loc = ahead().loc(); + if (allow_wildcard && accept(Token::Mul)) + return ast::Identifier(prev_loc, "*"); return accept(Token::Super) ? ast::Identifier(prev_loc, "super") : parse_id(); } From f6ca46f378e17208d1152999bd1fd43812431f99 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:47:42 +0200 Subject: [PATCH 06/14] hacky support for wildcard binders in 'use' declarations --- include/artic/ast.h | 2 ++ src/bind.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- src/check.cpp | 2 ++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/include/artic/ast.h b/include/artic/ast.h index 84adbd72..75686491 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -1533,6 +1533,8 @@ struct ModDecl : public NamedDecl { struct UseDecl : public NamedDecl { Path path; + PtrVector wildcard_imports; + // Set during type-checking bool is_value_ = false; diff --git a/src/bind.cpp b/src/bind.cpp index b38f8271..48cb2149 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -536,12 +536,54 @@ std::optional ModDecl::find_member(const std::string_view& name) con void UseDecl::bind_head(NameBinder& binder) { if (id.name != "") binder.insert_symbol(*this); - else + else if (path.elems.back().id.name != "*") binder.insert_symbol(*this, path.elems.back().id.name); + else { + path.bind(binder); + NamedDecl* decl = path.start_decl; + ModDecl* mod = nullptr; + for (size_t i = 1; i < path.elems.size(); i++) { + if ((mod = decl->isa())) { + if (i == path.elems.size() - 1) + break; + auto member = mod->find_member(path.elems[i].id.name); + if (member) { + decl = *member; + } else { + binder.error(path.elems[i].id.loc, "no member '{}' in '{}'", path.elems[i].id.name, mod->id.name); + return; + } + } else { + binder.error(path.elems[i].id.loc, "'{}' is not a module", decl); + } + } + + for (auto& decl : mod->decls) { + auto member = decl->isa(); + if (!member) + continue; + std::vector member_path_elements; + for (auto& elem : path.elems) { + assert(elem.args.empty()); + auto nelem = Path::Elem(elem.loc, std::move(Identifier(elem.id)), std::move(PtrVector())); + member_path_elements.emplace_back(std::move(nelem)); + } + member_path_elements.back().id.name = member->id.name; + Path member_path(path.loc, std::move(member_path_elements)); + Identifier nid = member->id; + wildcard_imports.push_back(std::make_unique(loc, std::move(member_path), std::move(nid))); + wildcard_imports.back()->bind_head(binder); + } + + } } void UseDecl::bind(NameBinder& binder) { path.bind(binder); + + for (auto& m : wildcard_imports) { + m->bind(binder); + } } void ErrorDecl::bind(NameBinder&) {} diff --git a/src/check.cpp b/src/check.cpp index 09abb9ed..a3b879f7 100644 --- a/src/check.cpp +++ b/src/check.cpp @@ -628,6 +628,8 @@ const artic::Type* Ptrn::check(TypeChecker& checker, const artic::Type* expected const artic::Type* Path::infer(TypeChecker& checker, std::optional value_expected, Ptr* arg) { if (!start_decl) return checker.type_table.type_error(); + if (elems.back().id.name == "*") + return nullptr; type = elems[0].is_super() ? checker.type_table.mod_type(*start_decl->as()) From 0badd8f9843f34db5d5979204aa103c0778e952d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:49:02 +0200 Subject: [PATCH 07/14] added a simple test for wildcard uses --- test/CMakeLists.txt | 1 + test/simple/use4.art | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/simple/use4.art diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c4daddb9..16fb079b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -65,6 +65,7 @@ add_test(NAME simple_mod3 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURC add_test(NAME simple_use1 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use1.art) add_test(NAME simple_use2 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use2.art) add_test(NAME simple_use3 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use3.art) +add_test(NAME simple_use4 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/use4.art) add_test(NAME simple_poly_fn1 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/poly_fn1.art) add_test(NAME simple_poly_fn2 COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/poly_fn2.art) add_test(NAME simple_nested_fns COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/nested_fns.art) diff --git a/test/simple/use4.art b/test/simple/use4.art new file mode 100644 index 00000000..5e8fb888 --- /dev/null +++ b/test/simple/use4.art @@ -0,0 +1,11 @@ +mod blob { + struct Blob { x: i32 } + fn g(x: i32, y: i32) = x + y; +} + +use blob::*; + +fn main(a: i32) -> Blob { + let x = g(a, -a); + Blob { x = x } +} From b6f40350b636e3332a3e17d99ea811e3d23c6265 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 18:52:01 +0200 Subject: [PATCH 08/14] document the new use capabilities in the README --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0da11500..1c6eb3c7 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ mod A { ```rust mod A { use super::C; - fn foo() { C::baz() } + fn foo() { baz() } mod B { fn bar() { super::foo() } } @@ -135,6 +135,17 @@ mod C { fn baz() { D::bar } } ``` + - Wildcard imports are now supported: +```rust +mod A { + fn f() -> i32 = 42; + fn g() -> i32 = 69; +} +use A::*; + +fn main() = f() + g(); +``` + - Tuples cannot be indexed with constant integers anymore: ```rust let t = (1, 2); From a54b74f08b5acb1360d81156125f6c4406740179 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 16:02:09 +0200 Subject: [PATCH 09/14] bind Path during NameBinding --- include/artic/ast.h | 12 ++- include/artic/bind.h | 2 + src/bind.cpp | 181 ++++++++++++++++++++++++++++++------------- 3 files changed, 138 insertions(+), 57 deletions(-) diff --git a/include/artic/ast.h b/include/artic/ast.h index 75686491..18f5474f 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -51,6 +51,7 @@ struct Node : public Cast { /// Location of the node in the source file. Loc loc; + mutable bool bound = false; /// Type assigned after type inference. Not all nodes are typeable. mutable const artic::Type* type = nullptr; /// IR definition assigned after IR emission. @@ -172,12 +173,16 @@ struct Path : public Node { Identifier id; PtrVector args; + // Set during name-binding + NamedDecl* decl = nullptr; + // These members are set during type-checking const artic::Type* type = nullptr; size_t index = 0; std::vector inferred_args; bool is_super() const { return id.name == "super"; } + bool is_wildcard() const { return id.name == "*"; } Elem(const Loc& loc, Identifier&& id, PtrVector&& args) : loc(loc), id(std::move(id)), args(std::move(args)) @@ -189,7 +194,8 @@ struct Path : public Node { // Set during name-binding, corresponds to the declaration that // is associated with the _first_ element of the path. // The rest of the path is resolved during type-checking. - ast::NamedDecl* start_decl; + ast::NamedDecl* start_decl = nullptr; + ast::NamedDecl* decl = nullptr; // Set during type-checking bool is_value = false; @@ -1468,6 +1474,8 @@ struct EnumDecl : public CtorDecl { , options(std::move(options)) {} + std::optional find_member(const std::string_view&) const; + const thorin::Def* emit(Emitter&) const override; const artic::Type* infer(TypeChecker&) override; void bind_head(NameBinder&) override; @@ -1533,6 +1541,7 @@ struct ModDecl : public NamedDecl { struct UseDecl : public NamedDecl { Path path; + NamedDecl* bound_to; PtrVector wildcard_imports; // Set during type-checking @@ -1549,6 +1558,7 @@ struct UseDecl : public NamedDecl { void resolve_summons(Summoner&) override {}; void print(Printer&) const override; + void bind_wildcard(NameBinder&); bool is_value() override; }; diff --git a/include/artic/bind.h b/include/artic/bind.h index 70b48b0b..d8e15ffa 100644 --- a/include/artic/bind.h +++ b/include/artic/bind.h @@ -73,6 +73,8 @@ class NameBinder : public Logger { return best; } + void unknown_member(const Loc&, const ast::NamedDecl*, const std::string_view&); + private: // Levenshtein distance is used to suggest similar identifiers to the user static constexpr size_t levenshtein_threshold() { return 3; } diff --git a/src/bind.cpp b/src/bind.cpp index 48cb2149..a2c3f9e1 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -13,9 +13,12 @@ void NameBinder::bind_head(ast::Decl& decl) { } void NameBinder::bind(ast::Node& node) { + if (node.bound) + return; if (node.attrs) node.attrs->bind(*this); node.bind(*this); + node.bound = true; } void NameBinder::pop_scope() { @@ -51,30 +54,78 @@ void NameBinder::insert_symbol(ast::NamedDecl& decl, const std::string& name) { } } +void NameBinder::unknown_member(const Loc& loc, const ast::NamedDecl* user_type, const std::string_view& member) { + if (auto mod_type = user_type->isa(); mod_type && mod_type->id.name == "") + error(loc, "no member '{}' in top-level module", member); + else + error(loc, "no member '{}' in '{}'", member, *user_type); +} + namespace ast { // Path ---------------------------------------------------------------------------- void Path::bind(NameBinder& binder) { - // Bind the first element of the path - auto& first = elems.front(); - if (first.id.name[0] == '_') - binder.error(first.id.loc, "identifiers beginning with '_' cannot be referenced"); - else if (first.is_super()) { - start_decl = binder.cur_mod->super; - if (!start_decl) - binder.error(first.id.loc, "top-level module has no super-module"); - } else { - auto symbol = binder.find_symbol(first.id.name); - if (!symbol) { - binder.error(first.id.loc, "unknown identifier '{}'", first.id.name); - if (auto similar = binder.find_similar_symbol(first.id.name)) - binder.note("did you mean '{}'?", similar->decl->id.name); - } else - start_decl = symbol->decl; - } - // Bind the type arguments of each element + decl = binder.cur_mod; for (auto& elem : elems) { + assert(decl); + //while (auto use = decl->isa()) { + // decl = use.decl; + //} + + if (elem.id.name[0] == '_') + binder.error(elem.id.loc, "identifiers beginning with '_' cannot be referenced"); + else if (elem.is_super()) { + if (auto mod = decl->isa()) { + if (!mod->super) + binder.error(elem.id.loc, "top-level module has no super-module"); + else + decl = mod->super; + } else + binder.error(elem.id.loc, "''super' can only be used on modules"); + } else if (elem.is_wildcard()) { + if (!start_decl) { + binder.error(elem.loc, "wildcards cannot appear at the start of a path!"); + return; + } + decl = nullptr; + } else if (decl == binder.cur_mod) { + auto symbol = binder.find_symbol(elem.id.name); + if (!symbol) { + binder.error(elem.id.loc, "unknown identifier '{}'", elem.id.name); + if (auto similar = binder.find_similar_symbol(elem.id.name)) + binder.note("did you mean '{}'?", similar->decl->id.name); + } else + decl = symbol->decl; + } else if (auto mod = decl->isa()) { + auto member = mod->find_member(elem.id.name); + if (!member) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; + } + decl = *member; + } else if (auto use = decl->isa()) { + binder.bind(*use); + assert(use->bound_to); + decl = use->bound_to; + } else if (auto enu = decl->isa()) { + auto found = enu->find_member(elem.id.name); + if (!found) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; + } + decl = *found; + } else { + // ... + assert(false); + } + + if (!start_decl) + start_decl = decl; + + elem.decl = decl; + + // Bind the type arguments of each element for (auto& arg : elem.args) binder.bind(*arg); } @@ -519,6 +570,10 @@ void ModDecl::bind(NameBinder& binder) { binder.cur_mod = this; binder.push_scope(); for (auto& decl : decls) binder.bind_head(*decl); + for (auto& decl : decls) { + if (auto use = decl->isa()) + use->bind_wildcard(binder); + } for (auto& decl : decls) binder.bind(*decl); std::swap(binder.scopes_, old_scopes); binder.cur_mod = old_mod; @@ -533,53 +588,67 @@ std::optional ModDecl::find_member(const std::string_view& name) con return std::nullopt; } -void UseDecl::bind_head(NameBinder& binder) { - if (id.name != "") - binder.insert_symbol(*this); - else if (path.elems.back().id.name != "*") - binder.insert_symbol(*this, path.elems.back().id.name); - else { - path.bind(binder); - NamedDecl* decl = path.start_decl; - ModDecl* mod = nullptr; - for (size_t i = 1; i < path.elems.size(); i++) { - if ((mod = decl->isa())) { - if (i == path.elems.size() - 1) - break; - auto member = mod->find_member(path.elems[i].id.name); - if (member) { - decl = *member; - } else { - binder.error(path.elems[i].id.loc, "no member '{}' in '{}'", path.elems[i].id.name, mod->id.name); - return; - } +std::optional EnumDecl::find_member(const std::string_view& name) const { + for (const auto& decl : options) { + if (decl->id.name == name) + return std::make_optional(&*decl); + } + return std::nullopt; +} + +void UseDecl::bind_wildcard(artic::NameBinder& binder) { + if (path.elems.back().id.name != "*") + return; + + binder.bind(path); + NamedDecl* decl = path.start_decl; + ModDecl* mod = nullptr; + for (size_t i = 1; i < path.elems.size(); i++) { + if ((mod = decl->isa())) { + if (i == path.elems.size() - 1) + break; + auto member = mod->find_member(path.elems[i].id.name); + if (member) { + decl = *member; } else { - binder.error(path.elems[i].id.loc, "'{}' is not a module", decl); + binder.error(path.elems[i].id.loc, "no member '{}' in '{}'", path.elems[i].id.name, mod->id.name); + return; } + } else { + binder.error(path.elems[i].id.loc, "'{}' is not a module", decl); } + } - for (auto& decl : mod->decls) { - auto member = decl->isa(); - if (!member) - continue; - std::vector member_path_elements; - for (auto& elem : path.elems) { - assert(elem.args.empty()); - auto nelem = Path::Elem(elem.loc, std::move(Identifier(elem.id)), std::move(PtrVector())); - member_path_elements.emplace_back(std::move(nelem)); - } - member_path_elements.back().id.name = member->id.name; - Path member_path(path.loc, std::move(member_path_elements)); - Identifier nid = member->id; - wildcard_imports.push_back(std::make_unique(loc, std::move(member_path), std::move(nid))); - wildcard_imports.back()->bind_head(binder); + for (auto& decl : mod->decls) { + auto member = decl->isa(); + if (!member) + continue; + std::vector member_path_elements; + for (auto& elem : path.elems) { + assert(elem.args.empty()); + auto nelem = Path::Elem(elem.loc, std::move(Identifier(elem.id)), std::move(PtrVector())); + member_path_elements.emplace_back(std::move(nelem)); } - + member_path_elements.back().id.name = member->id.name; + Path member_path(path.loc, std::move(member_path_elements)); + Identifier nid = member->id; + wildcard_imports.push_back(std::make_unique(loc, std::move(member_path), std::move(nid))); + wildcard_imports.back()->bind_head(binder); } } +void UseDecl::bind_head(NameBinder& binder) { + if (id.name != "") + binder.insert_symbol(*this); + else if (path.elems.back().id.name != "*") + binder.insert_symbol(*this, path.elems.back().id.name); +} + void UseDecl::bind(NameBinder& binder) { - path.bind(binder); + binder.bind(path); + + if (path.decl) + bound_to = path.decl; for (auto& m : wildcard_imports) { m->bind(binder); From 673aa3c03b418165f8ec72d291a841464d30b922 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 16:42:52 +0200 Subject: [PATCH 10/14] move the is_value logic out of Path::infer --- include/artic/ast.h | 5 ++- src/bind.cpp | 97 +++++++++++++++++++++++---------------------- src/check.cpp | 54 ++++++++++++++----------- src/emit.cpp | 11 +++-- 4 files changed, 91 insertions(+), 76 deletions(-) diff --git a/include/artic/ast.h b/include/artic/ast.h index 18f5474f..10c2fdb5 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -205,9 +205,10 @@ struct Path : public Node { : Node(loc), elems(std::move(elems)) {} - const artic::Type* infer(TypeChecker&, std::optional, Ptr* = nullptr); + const artic::Type* infer(TypeChecker&, Ptr*); + const artic::Type* infer(TypeChecker&, bool, Ptr* = nullptr); const artic::Type* infer(TypeChecker& checker) override { - return infer(checker, std::nullopt, nullptr); + return infer(checker, nullptr); } const thorin::Def* emit(Emitter&) const override; diff --git a/src/bind.cpp b/src/bind.cpp index a2c3f9e1..66911f31 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -69,55 +69,56 @@ void Path::bind(NameBinder& binder) { decl = binder.cur_mod; for (auto& elem : elems) { assert(decl); - //while (auto use = decl->isa()) { - // decl = use.decl; - //} - - if (elem.id.name[0] == '_') - binder.error(elem.id.loc, "identifiers beginning with '_' cannot be referenced"); - else if (elem.is_super()) { - if (auto mod = decl->isa()) { - if (!mod->super) - binder.error(elem.id.loc, "top-level module has no super-module"); - else - decl = mod->super; - } else - binder.error(elem.id.loc, "''super' can only be used on modules"); - } else if (elem.is_wildcard()) { - if (!start_decl) { - binder.error(elem.loc, "wildcards cannot appear at the start of a path!"); - return; - } - decl = nullptr; - } else if (decl == binder.cur_mod) { - auto symbol = binder.find_symbol(elem.id.name); - if (!symbol) { - binder.error(elem.id.loc, "unknown identifier '{}'", elem.id.name); - if (auto similar = binder.find_similar_symbol(elem.id.name)) - binder.note("did you mean '{}'?", similar->decl->id.name); - } else - decl = symbol->decl; - } else if (auto mod = decl->isa()) { - auto member = mod->find_member(elem.id.name); - if (!member) { - binder.unknown_member(elem.loc, mod, elem.id.name); - return; - } - decl = *member; - } else if (auto use = decl->isa()) { - binder.bind(*use); - assert(use->bound_to); - decl = use->bound_to; - } else if (auto enu = decl->isa()) { - auto found = enu->find_member(elem.id.name); - if (!found) { - binder.unknown_member(elem.loc, mod, elem.id.name); - return; + + while (true) { + if (elem.id.name[0] == '_') + binder.error(elem.id.loc, "identifiers beginning with '_' cannot be referenced"); + else if (elem.is_super()) { + if (auto mod = decl->isa()) { + if (!mod->super) + binder.error(elem.id.loc, "top-level module has no super-module"); + else + decl = mod->super; + } else + binder.error(elem.id.loc, "''super' can only be used on modules"); + } else if (elem.is_wildcard()) { + if (!start_decl) { + binder.error(elem.loc, "wildcards cannot appear at the start of a path!"); + return; + } + decl = nullptr; + } else if (decl == binder.cur_mod) { + auto symbol = binder.find_symbol(elem.id.name); + if (!symbol) { + binder.error(elem.id.loc, "unknown identifier '{}'", elem.id.name); + if (auto similar = binder.find_similar_symbol(elem.id.name)) + binder.note("did you mean '{}'?", similar->decl->id.name); + } else + decl = symbol->decl; + } else if (auto mod = decl->isa()) { + auto member = mod->find_member(elem.id.name); + if (!member) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; + } + decl = *member; + } else if (auto use = decl->isa()) { + binder.bind(*use); + assert(use->bound_to); + decl = use->bound_to; + continue; + } else if (auto enu = decl->isa()) { + auto found = enu->find_member(elem.id.name); + if (!found) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; + } + decl = *found; + } else { + // ... + assert(false); } - decl = *found; - } else { - // ... - assert(false); + break; } if (!start_decl) diff --git a/src/check.cpp b/src/check.cpp index a3b879f7..6d4eb3d3 100644 --- a/src/check.cpp +++ b/src/check.cpp @@ -625,11 +625,11 @@ const artic::Type* Ptrn::check(TypeChecker& checker, const artic::Type* expected // Path ---------------------------------------------------------------------------- -const artic::Type* Path::infer(TypeChecker& checker, std::optional value_expected, Ptr* arg) { - if (!start_decl) - return checker.type_table.type_error(); - if (elems.back().id.name == "*") +const artic::Type* Path::infer(TypeChecker& checker, Ptr* arg) { + if (elems.back().is_wildcard()) return nullptr; + if (!decl) + return checker.type_table.type_error(); type = elems[0].is_super() ? checker.type_table.mod_type(*start_decl->as()) @@ -673,21 +673,6 @@ const artic::Type* Path::infer(TypeChecker& checker, std::optional value_e } elem.type = type; - // Treat tuple-like structure constructors as functions - if (auto [type_app, struct_type] = match_app(type); - is_ctor && (*value_expected && value_expected) && struct_type && struct_type->is_tuple_like()) { - if (struct_type->member_count() > 0) { - SmallArray tuple_args(struct_type->member_count()); - for (size_t i = 0, n = struct_type->member_count(); i < n; ++i) - tuple_args[i] = member_type(type_app, struct_type, i); - auto dom = struct_type->member_count() == 1 - ? tuple_args.front() - : checker.type_table.tuple_type(tuple_args); - type = checker.type_table.fn_type(dom, type); - } - is_value = true; - } - // Perform a lookup inside the current object if the path is not finished if (i != n - 1) { if (elems[i + 1].is_super()) { @@ -732,10 +717,32 @@ const artic::Type* Path::infer(TypeChecker& checker, std::optional value_e } } - if (value_expected && is_value != *value_expected) { - checker.error(loc, "{} expected, but got '{}'", *value_expected ? "value" : "type", *this); + return type; +} + +const artic::Type* Path::infer(artic::TypeChecker& checker, bool value_expected, Ptr* arg) { + infer(checker, arg); + + // Treat tuple-like structure constructors as functions + if (auto [type_app, struct_type] = match_app(type); + is_ctor && value_expected && struct_type && struct_type->is_tuple_like()) { + if (struct_type->member_count() > 0) { + SmallArray tuple_args(struct_type->member_count()); + for (size_t i = 0, n = struct_type->member_count(); i < n; ++i) + tuple_args[i] = member_type(type_app, struct_type, i); + auto dom = struct_type->member_count() == 1 + ? tuple_args.front() + : checker.type_table.tuple_type(tuple_args); + type = checker.type_table.fn_type(dom, type); + } + is_value = true; + } + + if (is_value != value_expected) { + checker.error(loc, "{} expected, but got '{}'", value_expected ? "value" : "type", *this); return checker.type_table.type_error(); } + return type; } @@ -1734,7 +1741,8 @@ const artic::Type* UseDecl::infer(TypeChecker& checker) { if (!checker.enter_decl(this)) return checker.type_table.type_error(); auto path_type = checker.infer(path); - is_value_ = path.is_value; + if (path.decl) + is_value_ = path.decl->is_value(); checker.exit_decl(this); return path_type; } @@ -1800,7 +1808,7 @@ const artic::Type* RecordPtrn::infer(TypeChecker& checker) { const artic::Type* CtorPtrn::infer(TypeChecker& checker) { auto path_type = path.infer(checker, true); - if (!path.is_ctor) { + if (!path.decl->isa()) { checker.error(path.loc, "structure or enumeration constructor expected"); return checker.type_table.type_error(); } diff --git a/src/emit.cpp b/src/emit.cpp index 1080d350..197c38da 100644 --- a/src/emit.cpp +++ b/src/emit.cpp @@ -1071,10 +1071,12 @@ const thorin::Def* Path::emit(Emitter& emitter) const { for (size_t i = 0, n = elems.size(); i < n; ++i) { if (elems[i].is_super()) decl = i == 0 ? start_decl : decl->as()->super; + if (elems[i].is_wildcard()) + return nullptr; if (auto mod_type = elems[i].type->isa()) { decl = &mod_type->member(elems[i + 1].index); - } else if (!is_ctor) { + } else if (!elems[i].decl->isa()) { // If type arguments are present, this is a polymorphic application std::unordered_map map; if (!elems[i].inferred_args.empty()) { @@ -1756,14 +1758,17 @@ const thorin::Def* ModDecl::emit(Emitter& emitter) const { // Likewise, we do not emit implicit declarations if (auto implicit = decl->isa()) continue; + // Don't emit use declarations: they might point to modules. When used in expressions we'll emit them. + if (auto implicit = decl->isa()) + continue; emitter.emit(*decl); } return nullptr; } const thorin::Def* UseDecl::emit(Emitter& emitter) const { - if (path.is_value) - return path.emit(emitter); + if (path.decl) + return emitter.emit(*path.decl); return nullptr; } From 0c861b634f8d48ad78b7749e96af5987cb2ad901 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 17:32:05 +0200 Subject: [PATCH 11/14] use new Path::bind capability to resolve wildcards --- src/bind.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/bind.cpp b/src/bind.cpp index 66911f31..177d1043 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -602,22 +602,12 @@ void UseDecl::bind_wildcard(artic::NameBinder& binder) { return; binder.bind(path); - NamedDecl* decl = path.start_decl; - ModDecl* mod = nullptr; - for (size_t i = 1; i < path.elems.size(); i++) { - if ((mod = decl->isa())) { - if (i == path.elems.size() - 1) - break; - auto member = mod->find_member(path.elems[i].id.name); - if (member) { - decl = *member; - } else { - binder.error(path.elems[i].id.loc, "no member '{}' in '{}'", path.elems[i].id.name, mod->id.name); - return; - } - } else { - binder.error(path.elems[i].id.loc, "'{}' is not a module", decl); - } + assert(path.elems.size() >= 2); + auto& penultimate = path.elems[path.elems.size() - 2]; + NamedDecl* decl = penultimate.decl; + auto mod = decl->isa(); + if (!mod) { + binder.error(penultimate.id.loc, "'{}' is not a module", decl->id.name); } for (auto& decl : mod->decls) { From 7d44e9b31e1f50cb2be96b433072283a721b40bf Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 17:33:06 +0200 Subject: [PATCH 12/14] naming --- src/bind.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bind.cpp b/src/bind.cpp index 177d1043..63216e0c 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -604,13 +604,13 @@ void UseDecl::bind_wildcard(artic::NameBinder& binder) { binder.bind(path); assert(path.elems.size() >= 2); auto& penultimate = path.elems[path.elems.size() - 2]; - NamedDecl* decl = penultimate.decl; - auto mod = decl->isa(); - if (!mod) { - binder.error(penultimate.id.loc, "'{}' is not a module", decl->id.name); + NamedDecl* importee = penultimate.decl; + auto imported_mod = importee->isa(); + if (!imported_mod) { + binder.error(penultimate.id.loc, "'{}' is not a module", importee->id.name); } - for (auto& decl : mod->decls) { + for (auto& decl : imported_mod->decls) { auto member = decl->isa(); if (!member) continue; From a2a0c7c9b8a10d7826166ec583e082e4d22cc626 Mon Sep 17 00:00:00 2001 From: Gobrosse Date: Tue, 9 Jul 2024 14:31:28 +0200 Subject: [PATCH 13/14] don't pre-resolve intermediary UseDecls in Path::bind --- src/bind.cpp | 102 ++++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/bind.cpp b/src/bind.cpp index 63216e0c..337346da 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -66,64 +66,68 @@ namespace ast { // Path ---------------------------------------------------------------------------- void Path::bind(NameBinder& binder) { - decl = binder.cur_mod; + NamedDecl* decl = binder.cur_mod; + size_t i = 0; for (auto& elem : elems) { assert(decl); - while (true) { - if (elem.id.name[0] == '_') - binder.error(elem.id.loc, "identifiers beginning with '_' cannot be referenced"); - else if (elem.is_super()) { - if (auto mod = decl->isa()) { - if (!mod->super) - binder.error(elem.id.loc, "top-level module has no super-module"); - else - decl = mod->super; - } else - binder.error(elem.id.loc, "''super' can only be used on modules"); - } else if (elem.is_wildcard()) { - if (!start_decl) { - binder.error(elem.loc, "wildcards cannot appear at the start of a path!"); - return; - } - decl = nullptr; - } else if (decl == binder.cur_mod) { - auto symbol = binder.find_symbol(elem.id.name); - if (!symbol) { - binder.error(elem.id.loc, "unknown identifier '{}'", elem.id.name); - if (auto similar = binder.find_similar_symbol(elem.id.name)) - binder.note("did you mean '{}'?", similar->decl->id.name); - } else - decl = symbol->decl; - } else if (auto mod = decl->isa()) { - auto member = mod->find_member(elem.id.name); - if (!member) { - binder.unknown_member(elem.loc, mod, elem.id.name); - return; - } - decl = *member; - } else if (auto use = decl->isa()) { - binder.bind(*use); - assert(use->bound_to); - decl = use->bound_to; - continue; - } else if (auto enu = decl->isa()) { - auto found = enu->find_member(elem.id.name); - if (!found) { - binder.unknown_member(elem.loc, mod, elem.id.name); - return; - } - decl = *found; - } else { - // ... - assert(false); + // We need to look inside UseDecls to bind paths properly. + while (auto use = decl->isa()) { + binder.bind(*use); + assert(use->bound_to); + decl = use->bound_to; + } + + if (elem.id.name[0] == '_') + binder.error(elem.id.loc, "identifiers beginning with '_' cannot be referenced"); + else if (elem.is_super()) { + if (auto mod = decl->isa()) { + if (!mod->super) + binder.error(elem.id.loc, "top-level module has no super-module"); + else + decl = mod->super; + } else + binder.error(elem.id.loc, "''super' can only be used on modules"); + } else if (elem.is_wildcard()) { + if (!start_decl) { + binder.error(elem.loc, "wildcards cannot appear at the start of a path!"); + return; + } + decl = nullptr; + } else if (i == 0) { + assert(decl == binder.cur_mod); + auto symbol = binder.find_symbol(elem.id.name); + if (!symbol) { + binder.error(elem.id.loc, "unknown identifier '{}'", elem.id.name); + if (auto similar = binder.find_similar_symbol(elem.id.name)) + binder.note("did you mean '{}'?", similar->decl->id.name); + } else + decl = symbol->decl; + } else if (auto mod = decl->isa()) { + auto member = mod->find_member(elem.id.name); + if (!member) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; } - break; + decl = *member; + } else if (auto enu = decl->isa()) { + auto found = enu->find_member(elem.id.name); + if (!found) { + binder.unknown_member(elem.loc, mod, elem.id.name); + return; + } + decl = *found; + } else { + // ... + assert(false); } if (!start_decl) start_decl = decl; + if (i++ == elems.size() - 1) + this->decl = decl; + elem.decl = decl; // Bind the type arguments of each element From a51f8be0b19e0d44e9a3b1ad8afa0309c5c29f4c Mon Sep 17 00:00:00 2001 From: Gobrosse Date: Tue, 9 Jul 2024 14:34:03 +0200 Subject: [PATCH 14/14] removed is_ctor member from Path --- include/artic/ast.h | 1 - src/check.cpp | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/artic/ast.h b/include/artic/ast.h index 10c2fdb5..58827c72 100644 --- a/include/artic/ast.h +++ b/include/artic/ast.h @@ -199,7 +199,6 @@ struct Path : public Node { // Set during type-checking bool is_value = false; - bool is_ctor = false; Path(const Loc& loc, std::vector&& elems) : Node(loc), elems(std::move(elems)) diff --git a/src/check.cpp b/src/check.cpp index 6d4eb3d3..d719f4ae 100644 --- a/src/check.cpp +++ b/src/check.cpp @@ -635,7 +635,6 @@ const artic::Type* Path::infer(TypeChecker& checker, Ptr* arg) { ? checker.type_table.mod_type(*start_decl->as()) : checker.infer(*start_decl); is_value = elems.size() == 1 && start_decl->is_value(); - is_ctor = start_decl->isa(); // Inspect every element of the path for (size_t i = 0, n = elems.size(); i < n; ++i) { @@ -693,11 +692,10 @@ const artic::Type* Path::infer(TypeChecker& checker, Ptr* arg) { if (type_app) type = checker.type_table.type_app(type->as(), type_app->type_args); is_value = false; - is_ctor = true; } else { auto member = member_type(type_app, enum_type, *index); type = is_unit_type(member) ? type : checker.type_table.fn_type(member, type); - is_value = is_ctor = true; + is_value = true; } } else if (auto mod_type = type->isa()) { auto index = mod_type->find_member(elems[i + 1].id.name); @@ -711,7 +709,6 @@ const artic::Type* Path::infer(TypeChecker& checker, Ptr* arg) { ? checker.type_table.mod_type(*member.as()) : checker.infer(mod_type->member(*index)); is_value = member.is_value(); - is_ctor = member.isa(); } else return checker.type_expected(elem.loc, type, "module or enum"); } @@ -723,6 +720,8 @@ const artic::Type* Path::infer(TypeChecker& checker, Ptr* arg) { const artic::Type* Path::infer(artic::TypeChecker& checker, bool value_expected, Ptr* arg) { infer(checker, arg); + auto is_ctor = decl->isa(); + // Treat tuple-like structure constructors as functions if (auto [type_app, struct_type] = match_app(type); is_ctor && value_expected && struct_type && struct_type->is_tuple_like()) {