aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-08-11 19:45:21 +0100
committerPhilip Herron <philip.herron@embecosm.com>2021-08-19 14:36:11 +0100
commit71cf0d44d3fceb235c95d5acaf92b498f8cf923b (patch)
tree4586cb6d436eeda82408da48709647f9f0cc5eb0 /gcc
parent3f877a690dccd5faf5b75ad7b75e0bd85f0a5be4 (diff)
downloadgcc-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.h213
-rw-r--r--gcc/rust/backend/rust-compile-resolve-path.cc31
-rw-r--r--gcc/rust/backend/rust-compile.cc28
-rw-r--r--gcc/rust/typecheck/rust-hir-trait-ref.h49
-rw-r--r--gcc/rust/typecheck/rust-hir-trait-resolve.cc64
-rw-r--r--gcc/rust/typecheck/rust-hir-trait-resolve.h5
-rw-r--r--gcc/testsuite/rust/compile/torture/traits10.rs32
-rw-r--r--gcc/testsuite/rust/compile/traits1.rs1
-rw-r--r--gcc/testsuite/rust/compile/traits2.rs1
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;