diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-08-11 19:45:21 +0100 |
---|---|---|
committer | Philip Herron <philip.herron@embecosm.com> | 2021-08-19 14:36:11 +0100 |
commit | 71cf0d44d3fceb235c95d5acaf92b498f8cf923b (patch) | |
tree | 4586cb6d436eeda82408da48709647f9f0cc5eb0 /gcc | |
parent | 3f877a690dccd5faf5b75ad7b75e0bd85f0a5be4 (diff) | |
download | gcc-71cf0d44d3fceb235c95d5acaf92b498f8cf923b.zip gcc-71cf0d44d3fceb235c95d5acaf92b498f8cf923b.tar.gz gcc-71cf0d44d3fceb235c95d5acaf92b498f8cf923b.tar.bz2 |
Add support for optional trait functions in method calls
This adds compilation support for optional trait functions with a function
block. This does not support constants or default types yet.
Trait references must do type resolution of their block, but this is only
performed once a trait is resolved. In order to avoid recursive trait
resolution we use a on_resolved callback to invoke this to occur. The trait
resolver works as a query based system so you have to avoid the case of
recursively resolving the trait untill it is stored in the internal
mappings.
Addresses #542
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/backend/rust-compile-implitem.h | 213 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-resolve-path.cc | 31 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile.cc | 28 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-ref.h | 49 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-resolve.cc | 64 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-resolve.h | 5 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/torture/traits10.rs | 32 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits1.rs | 1 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits2.rs | 1 |
9 files changed, 419 insertions, 5 deletions
diff --git a/gcc/rust/backend/rust-compile-implitem.h b/gcc/rust/backend/rust-compile-implitem.h index 7608599..e446cf9 100644 --- a/gcc/rust/backend/rust-compile-implitem.h +++ b/gcc/rust/backend/rust-compile-implitem.h @@ -297,6 +297,219 @@ private: TyTy::BaseType *concrete; }; +class CompileTraitItem : public HIRCompileBase +{ + using Rust::Compile::HIRCompileBase::visit; + +public: + static void Compile (TyTy::BaseType *self, HIR::TraitItem *item, Context *ctx, + TyTy::BaseType *concrete) + { + CompileTraitItem compiler (self, ctx, concrete); + item->accept_vis (compiler); + } + + void visit (HIR::TraitItemFunc &func) override + { + rust_assert (func.has_block_defined ()); + + rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *fntype = static_cast<TyTy::FnType *> (concrete); + + // items can be forward compiled which means we may not need to invoke this + // code. We might also have already compiled this generic function as well. + Bfunction *lookup = nullptr; + if (ctx->lookup_function_decl (fntype->get_ty_ref (), &lookup, fntype)) + { + // has this been added to the list then it must be finished + if (ctx->function_completed (lookup)) + { + Bfunction *dummy = nullptr; + if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &dummy)) + ctx->insert_function_decl (fntype->get_ty_ref (), lookup, fntype); + + return; + } + } + + if (fntype->has_subsititions_defined ()) + { + // override the Hir Lookups for the substituions in this context + fntype->override_context (); + } + + // convert to the actual function type + ::Btype *compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype); + + HIR::TraitFunctionDecl &function = func.get_decl (); + unsigned int flags = 0; + + const Resolver::CanonicalPath *canonical_path = nullptr; + rust_assert (ctx->get_mappings ()->lookup_canonical_path ( + func.get_mappings ().get_crate_num (), func.get_mappings ().get_nodeid (), + &canonical_path)); + + std::string fn_identifier = canonical_path->get (); + std::string asm_name + = ctx->mangle_impl_item (self, fntype, function.get_function_name ()); + + Bfunction *fndecl + = ctx->get_backend ()->function (compiled_fn_type, fn_identifier, + asm_name, flags, func.get_locus ()); + ctx->insert_function_decl (fntype->get_ty_ref (), fndecl, fntype); + + // setup the params + TyTy::BaseType *tyret = fntype->get_return_type (); + std::vector<Bvariable *> param_vars; + + if (function.is_method ()) + { + // insert self + TyTy::BaseType *self_tyty_lookup = nullptr; + if (!ctx->get_tyctx ()->lookup_type ( + function.get_self ().get_mappings ().get_hirid (), + &self_tyty_lookup)) + { + rust_error_at (function.get_self ().get_locus (), + "failed to lookup self param type"); + return; + } + + Btype *self_type = TyTyResolveCompile::compile (ctx, self_tyty_lookup); + if (self_type == nullptr) + { + rust_error_at (function.get_self ().get_locus (), + "failed to compile self param type"); + return; + } + + Bvariable *compiled_self_param + = CompileSelfParam::compile (ctx, fndecl, function.get_self (), + self_type, + function.get_self ().get_locus ()); + if (compiled_self_param == nullptr) + { + rust_error_at (function.get_self ().get_locus (), + "failed to compile self param variable"); + return; + } + + param_vars.push_back (compiled_self_param); + ctx->insert_var_decl (function.get_self ().get_mappings ().get_hirid (), + compiled_self_param); + } + + // offset from + 1 for the TyTy::FnType being used when this is a method to + // skip over Self on the FnType + size_t i = function.is_method () ? 1 : 0; + for (auto referenced_param : function.get_function_params ()) + { + auto tyty_param = fntype->param_at (i); + auto param_tyty = tyty_param.second; + + auto compiled_param_type + = TyTyResolveCompile::compile (ctx, param_tyty); + if (compiled_param_type == nullptr) + { + rust_error_at (referenced_param.get_locus (), + "failed to compile parameter type"); + return; + } + + Location param_locus + = ctx->get_mappings ()->lookup_location (param_tyty->get_ref ()); + Bvariable *compiled_param_var + = CompileFnParam::compile (ctx, fndecl, &referenced_param, + compiled_param_type, param_locus); + if (compiled_param_var == nullptr) + { + rust_error_at (param_locus, "Failed to compile parameter variable"); + return; + } + + param_vars.push_back (compiled_param_var); + + ctx->insert_var_decl (referenced_param.get_mappings ().get_hirid (), + compiled_param_var); + i++; + } + + if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) + { + rust_fatal_error (func.get_locus (), + "failed to setup parameter variables"); + return; + } + + // lookup locals + auto block_expr = func.get_block_expr ().get (); + auto body_mappings = block_expr->get_mappings (); + + Resolver::Rib *rib = nullptr; + if (!ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), + &rib)) + { + rust_fatal_error (func.get_locus (), + "failed to setup locals per block"); + return; + } + + std::vector<Bvariable *> locals; + bool ok = compile_locals_for_block (*rib, fndecl, locals); + rust_assert (ok); + + Bblock *enclosing_scope = NULL; + HIR::BlockExpr *function_body = func.get_block_expr ().get (); + Location start_location = function_body->get_locus (); + Location end_location = function_body->get_closing_locus (); + + Bblock *code_block + = ctx->get_backend ()->block (fndecl, enclosing_scope, locals, + start_location, end_location); + ctx->push_block (code_block); + + Bvariable *return_address = nullptr; + if (function.has_return_type ()) + { + Btype *return_type = TyTyResolveCompile::compile (ctx, tyret); + + bool address_is_taken = false; + Bstatement *ret_var_stmt = nullptr; + + return_address = ctx->get_backend ()->temporary_variable ( + fndecl, code_block, return_type, NULL, address_is_taken, + func.get_locus (), &ret_var_stmt); + + ctx->add_statement (ret_var_stmt); + } + + ctx->push_fn (fndecl, return_address); + + compile_function_body (fndecl, func.get_block_expr (), + function.has_return_type ()); + + ctx->pop_block (); + auto body = ctx->get_backend ()->block_statement (code_block); + if (!ctx->get_backend ()->function_set_body (fndecl, body)) + { + rust_error_at (func.get_locus (), "failed to set body to function"); + return; + } + + ctx->pop_fn (); + ctx->push_function (fndecl); + } + +private: + CompileTraitItem (TyTy::BaseType *self, Context *ctx, + TyTy::BaseType *concrete) + : HIRCompileBase (ctx), self (self), concrete (concrete) + {} + + TyTy::BaseType *self; + TyTy::BaseType *concrete; +}; + } // namespace Compile } // namespace Rust diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc index 5f07a2a..97ba15d 100644 --- a/gcc/rust/backend/rust-compile-resolve-path.cc +++ b/gcc/rust/backend/rust-compile-resolve-path.cc @@ -138,8 +138,35 @@ ResolvePathRef::visit (HIR::PathInExpression &expr) { // this means we are defaulting back to the trait_item if // possible - // TODO - gcc_unreachable (); + Resolver::TraitItemReference *trait_item_ref = nullptr; + bool ok = trait_ref->lookup_hir_trait_item (*trait_item, + &trait_item_ref); + rust_assert (ok); // found + rust_assert ( + trait_item_ref->is_optional ()); // has definition + + // FIXME + // TyTy::BaseType *self_type = nullptr; + // if (!ctx->get_tyctx ()->lookup_type ( + // expr.get_receiver ()->get_mappings ().get_hirid (), + // &self_type)) + // { + // rust_error_at (expr.get_locus (), + // "failed to resolve type for self param"); + // return; + // } + + // CompileTraitItem::Compile ( + // self_type, trait_item_ref->get_hir_trait_item (), ctx, + // fntype); + // if (!ctx->lookup_function_decl (fntype->get_ty_ref (), + // &fn)) + // { + // translated = ctx->get_backend ()->error_expression (); + // rust_error_at (expr.get_locus (), + // "forward declaration was not compiled"); + // return; + // } } else { diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc index b9b5084..c36f848 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -181,8 +181,32 @@ CompileExpr::visit (HIR::MethodCallExpr &expr) { // this means we are defaulting back to the trait_item if // possible - // TODO - gcc_unreachable (); + Resolver::TraitItemReference *trait_item_ref = nullptr; + bool ok = trait_ref->lookup_hir_trait_item (*trait_item, + &trait_item_ref); + rust_assert (ok); // found + rust_assert (trait_item_ref->is_optional ()); // has definition + + TyTy::BaseType *self_type = nullptr; + if (!ctx->get_tyctx ()->lookup_type ( + expr.get_receiver ()->get_mappings ().get_hirid (), + &self_type)) + { + rust_error_at (expr.get_locus (), + "failed to resolve type for self param"); + return; + } + + CompileTraitItem::Compile (self_type, + trait_item_ref->get_hir_trait_item (), + ctx, fntype); + if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &fn)) + { + translated = ctx->get_backend ()->error_expression (); + rust_error_at (expr.get_locus (), + "forward declaration was not compiled"); + return; + } } else { diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index 21a8beb..718b3ce 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -111,9 +111,11 @@ public: const HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; } + HIR::TraitItem *get_hir_trait_item () { return hir_trait_item; } + Location get_locus () const { return locus; } - const Analysis::NodeMapping &get_mappings () const + const Analysis::NodeMapping get_mappings () const { return hir_trait_item->get_mappings (); } @@ -146,6 +148,13 @@ public: return get_error (); } + // this is called when the trait is completed resolution and gives the items a + // chance to run their specific type resolution passes. If we call their + // resolution on construction it can lead to a case where the trait being + // resolved recursively trying to resolve the trait itself infinitely since + // the trait will not be stored in its own map yet + void on_resolved (); + private: TyTy::ErrorType *get_error () const { @@ -160,6 +169,11 @@ private: TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const; + bool is_item_resolved () const; + void resolve_item (HIR::TraitItemType &type); + void resolve_item (HIR::TraitItemConst &constant); + void resolve_item (HIR::TraitItemFunc &func); + std::string identifier; bool optional_flag; TraitItemType type; @@ -233,6 +247,31 @@ public: return hir_trait_ref->get_mappings (); } + bool lookup_hir_trait_item (const HIR::TraitItem &item, + TraitItemReference **ref) + { + return lookup_trait_item (item.trait_identifier (), ref); + } + + bool lookup_trait_item (const std::string &ident, TraitItemReference **ref) + { + for (auto &item : item_refs) + { + if (ident.compare (item.get_identifier ()) == 0) + { + *ref = &item; + return true; + } + } + return false; + } + + bool lookup_hir_trait_item (const HIR::TraitItem &item, + const TraitItemReference **ref) const + { + return lookup_trait_item (item.trait_identifier (), ref); + } + bool lookup_trait_item (const std::string &ident, const TraitItemReference **ref) const { @@ -269,6 +308,14 @@ public: return item_refs; } + void on_resolved () + { + for (auto &item : item_refs) + { + item.on_resolved (); + } + } + private: const HIR::Trait *hir_trait_ref; std::vector<TraitItemReference> item_refs; diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc index 5356636..d760709 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.cc +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc @@ -17,6 +17,7 @@ // <http://www.gnu.org/licenses/>. #include "rust-hir-trait-resolve.h" +#include "rust-hir-type-check-expr.h" namespace Rust { namespace Resolver { @@ -64,5 +65,68 @@ ResolveTraitItemToRef::visit (HIR::TraitItemFunc &fn) self, substitutions, locus); } +// TraitItemReference items + +void +TraitItemReference::on_resolved () +{ + switch (type) + { + case CONST: + resolve_item (static_cast<HIR::TraitItemConst &> (*hir_trait_item)); + break; + + case TYPE: + resolve_item (static_cast<HIR::TraitItemType &> (*hir_trait_item)); + break; + + case FN: + resolve_item (static_cast<HIR::TraitItemFunc &> (*hir_trait_item)); + break; + + default: + break; + } +} + +void +TraitItemReference::resolve_item (HIR::TraitItemType &type) +{ + // TODO +} + +void +TraitItemReference::resolve_item (HIR::TraitItemConst &constant) +{ + // TODO +} + +void +TraitItemReference::resolve_item (HIR::TraitItemFunc &func) +{ + if (!is_optional ()) + return; + + TyTy::BaseType *item_tyty = get_tyty (); + if (item_tyty->get_kind () == TyTy::TypeKind::ERROR) + return; + + // check the block and return types + rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF); + + // need to get the return type from this + TyTy::FnType *resolved_fn_type = static_cast<TyTy::FnType *> (item_tyty); + auto expected_ret_tyty = resolved_fn_type->get_return_type (); + context->push_return_type (expected_ret_tyty); + + auto block_expr_ty + = TypeCheckExpr::Resolve (func.get_block_expr ().get (), false); + + context->pop_return_type (); + + if (block_expr_ty->get_kind () != TyTy::NEVER) + expected_ret_tyty->unify (block_expr_ty); +} + } // namespace Resolver } // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h index 6507bca..6874a3a 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.h +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -154,6 +154,11 @@ private: trait_reference->get_mappings ().get_defid (), &tref); rust_assert (ok); + // hook to allow the trait to resolve its optional item blocks, we cant + // resolve the blocks of functions etc because it can end up in a recursive + // loop of trying to resolve traits as required by the types + tref->on_resolved (); + return tref; } diff --git a/gcc/testsuite/rust/compile/torture/traits10.rs b/gcc/testsuite/rust/compile/torture/traits10.rs new file mode 100644 index 0000000..3e47b1b --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/traits10.rs @@ -0,0 +1,32 @@ +trait Foo +where + Self: Sized, +{ + fn get(self) -> i32; + // { dg-warning "unused name" "" { target *-*-* } .-1 } + + fn test(self) -> i32 { + self.get() + } +} + +struct Bar(i32); +impl Foo for Bar { + fn get(self) -> i32 { + self.0 + } +} + +fn main() { + let a; + a = Bar(123); + + let b; + b = Bar::get(a); + + let a; + a = Bar(123); + + let b; + b = a.test(); +} diff --git a/gcc/testsuite/rust/compile/traits1.rs b/gcc/testsuite/rust/compile/traits1.rs index 30483b1..355064e 100644 --- a/gcc/testsuite/rust/compile/traits1.rs +++ b/gcc/testsuite/rust/compile/traits1.rs @@ -1,5 +1,6 @@ trait Foo { fn Bar() -> i32 {} + // { dg-error "expected .i32. got .()." "" { target *-*-* } .-1 } } struct Baz; diff --git a/gcc/testsuite/rust/compile/traits2.rs b/gcc/testsuite/rust/compile/traits2.rs index 08e6bd3..7357c22 100644 --- a/gcc/testsuite/rust/compile/traits2.rs +++ b/gcc/testsuite/rust/compile/traits2.rs @@ -1,5 +1,6 @@ trait Foo { fn Bar() -> i32 {} + // { dg-error "expected .i32. got .()." "" { target *-*-* } .-1 } } struct Baz; |