aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-06-29 17:20:47 +0100
committerPhilip Herron <philip.herron@embecosm.com>2021-06-30 17:31:41 +0100
commit9e7e6b3b8742a89b97323ec4a8e37be2c0f5a9a0 (patch)
tree2a29dac59f9d376f554172197539e51d918af283
parent0163ca4b6f2248103d416405b6758f7abb5e18ab (diff)
downloadgcc-9e7e6b3b8742a89b97323ec4a8e37be2c0f5a9a0.zip
gcc-9e7e6b3b8742a89b97323ec4a8e37be2c0f5a9a0.tar.gz
gcc-9e7e6b3b8742a89b97323ec4a8e37be2c0f5a9a0.tar.bz2
Implicit Self on Traits
Self is actually an implicit inherited type parameter on Trait declarations. This adds this akin to impl blocks, and substitutes the actual impl self type into the trait-impl item for type checking. Fixes #473
-rw-r--r--gcc/rust/ast/rust-item.h24
-rw-r--r--gcc/rust/hir/tree/rust-hir-item.h31
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-item.h86
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-type.h10
-rw-r--r--gcc/rust/typecheck/rust-hir-trait-resolve.h63
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-implitem.h98
-rw-r--r--gcc/rust/typecheck/rust-tyty.h1
-rw-r--r--gcc/testsuite/rust/compile/torture/traits3.rs18
8 files changed, 263 insertions, 68 deletions
diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h
index f9a5a6d..7865a44 100644
--- a/gcc/rust/ast/rust-item.h
+++ b/gcc/rust/ast/rust-item.h
@@ -3382,24 +3382,11 @@ class Trait : public VisItem
{
bool has_unsafe;
Identifier name;
-
- // bool has_generics;
- // Generics generic_params;
- std::vector<std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- std::vector<std::unique_ptr<TypeParamBound> >
- type_param_bounds; // inlined form
-
- // bool has_where_clause;
+ std::vector<std::unique_ptr<GenericParam> > generic_params;
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
WhereClause where_clause;
-
std::vector<Attribute> inner_attrs;
-
- // bool has_trait_items;
std::vector<std::unique_ptr<TraitItem> > trait_items;
-
Location locus;
public:
@@ -3432,7 +3419,6 @@ public:
std::vector<std::unique_ptr<TraitItem> > trait_items, Visibility vis,
std::vector<Attribute> outer_attrs, std::vector<Attribute> inner_attrs,
Location locus)
-
: VisItem (std::move (vis), std::move (outer_attrs)),
has_unsafe (is_unsafe), name (std::move (name)),
generic_params (std::move (generic_params)),
@@ -3530,13 +3516,17 @@ public:
return type_param_bounds;
}
- // TODO: is this better? Or is a "vis_block" better?
WhereClause &get_where_clause ()
{
rust_assert (has_where_clause ());
return where_clause;
}
+ void insert_implict_self (std::unique_ptr<AST::GenericParam> &&param)
+ {
+ generic_params.push_back (std::move (param));
+ }
+
protected:
/* Use covariance to implement clone function as returning this object
* rather than base */
diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h
index 0cf71e7..665ca2a 100644
--- a/gcc/rust/hir/tree/rust-hir-item.h
+++ b/gcc/rust/hir/tree/rust-hir-item.h
@@ -385,7 +385,7 @@ public:
bool has_lifetime () const { return !lifetime.is_error (); }
// Returns whether the self-param is in an error state.
- bool is_error () const { return self_kind != ImplicitSelfKind::NONE; }
+ bool is_error () const { return self_kind == ImplicitSelfKind::NONE; }
std::string as_string () const;
@@ -2364,7 +2364,7 @@ public:
generic_params (std::move (generic_params)),
function_params (std::move (function_params)),
return_type (std::move (return_type)),
- where_clause (std::move (where_clause)), self (self)
+ where_clause (std::move (where_clause)), self (std::move (self))
{}
// Copy constructor with clone
@@ -2661,24 +2661,11 @@ protected:
class Trait : public VisItem
{
bool has_unsafe;
-
Identifier name;
-
- // bool has_generics;
- // Generics generic_params;
- std::vector<std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- std::vector<std::unique_ptr<TypeParamBound> >
- type_param_bounds; // inlined form
-
- // bool has_where_clause;
+ std::vector<std::unique_ptr<GenericParam> > generic_params;
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
WhereClause where_clause;
-
- // bool has_trait_items;
std::vector<std::unique_ptr<TraitItem> > trait_items;
-
Location locus;
public:
@@ -2768,6 +2755,16 @@ public:
void accept_vis (HIRVisitor &vis) override;
+ std::vector<std::unique_ptr<GenericParam> > &get_generic_params ()
+ {
+ return generic_params;
+ }
+
+ const std::vector<std::unique_ptr<GenericParam> > &get_generic_params () const
+ {
+ return generic_params;
+ }
+
protected:
/* Use covariance to implement clone function as returning this object
* rather than base */
diff --git a/gcc/rust/resolve/rust-ast-resolve-item.h b/gcc/rust/resolve/rust-ast-resolve-item.h
index 3800350..fafd27e 100644
--- a/gcc/rust/resolve/rust-ast-resolve-item.h
+++ b/gcc/rust/resolve/rust-ast-resolve-item.h
@@ -310,16 +310,15 @@ public:
NodeId scope_node_id = trait.get_node_id ();
resolver->get_type_scope ().push (scope_node_id);
- // TODO
// we need to inject an implicit self TypeParam here
- // see: https://doc.rust-lang.org/reference/items/traits.html
+ AST::TypeParam *implicit_self
+ = new AST::TypeParam ("Self", trait.get_locus ());
+ trait.insert_implict_self (
+ std::unique_ptr<AST::GenericParam> (implicit_self));
- if (trait.has_generics ())
+ for (auto &generic : trait.get_generic_params ())
{
- for (auto &generic : trait.get_generic_params ())
- {
- ResolveGenericParam::go (generic.get (), trait.get_node_id ());
- }
+ ResolveGenericParam::go (generic.get (), trait.get_node_id ());
}
for (auto &item : trait.get_trait_items ())
@@ -371,21 +370,74 @@ public:
resolver->get_label_scope ().pop ();
}
- void visit (AST::TraitItemMethod &) override
+ void visit (AST::TraitItemMethod &func) override
{
- // TODO
- }
+ NodeId scope_node_id = func.get_node_id ();
+ resolver->get_name_scope ().push (scope_node_id);
+ resolver->get_type_scope ().push (scope_node_id);
+ resolver->get_label_scope ().push (scope_node_id);
+ resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+ resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+ resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
- void visit (AST::TraitItemConst &) override
- {
- // TODO
- }
+ AST::TraitMethodDecl &function = func.get_trait_method_decl ();
+ if (function.has_generics ())
+ {
+ for (auto &generic : function.get_generic_params ())
+ ResolveGenericParam::go (generic.get (), func.get_node_id ());
+ }
- void visit (AST::TraitItemType &) override
- {
- // TODO
+ if (function.has_return_type ())
+ ResolveType::go (function.get_return_type ().get (), func.get_node_id ());
+
+ // self turns into (self: Self) as a function param
+ AST::SelfParam &self_param = function.get_self_param ();
+ AST::IdentifierPattern self_pattern (
+ self_param.get_node_id (), "self", self_param.get_locus (),
+ self_param.get_has_ref (), self_param.get_is_mut (),
+ std::unique_ptr<AST::Pattern> (nullptr));
+
+ std::vector<std::unique_ptr<AST::TypePathSegment> > segments;
+ segments.push_back (std::unique_ptr<AST::TypePathSegment> (
+ new AST::TypePathSegment ("Self", false, self_param.get_locus ())));
+
+ AST::TypePath self_type_path (std::move (segments),
+ self_param.get_locus ());
+
+ ResolveType::go (&self_type_path, self_param.get_node_id ());
+ PatternDeclaration::go (&self_pattern, self_param.get_node_id ());
+
+ resolver->mark_assignment_to_decl (self_pattern.get_node_id (),
+ self_pattern.get_node_id ());
+
+ // we make a new scope so the names of parameters are resolved and shadowed
+ // correctly
+ for (auto &param : function.get_function_params ())
+ {
+ ResolveType::go (param.get_type ().get (), param.get_node_id ());
+ PatternDeclaration::go (param.get_pattern ().get (),
+ param.get_node_id ());
+
+ // the mutability checker needs to verify for immutable decls the number
+ // of assignments are <1. This marks an implicit assignment
+ resolver->mark_assignment_to_decl (param.get_pattern ()->get_node_id (),
+ param.get_node_id ());
+ }
+
+ // trait items have an optional body
+ if (func.has_definition ())
+ ResolveExpr::go (func.get_definition ().get (), func.get_node_id ());
+
+ resolver->get_name_scope ().pop ();
+ resolver->get_type_scope ().pop ();
+ resolver->get_label_scope ().pop ();
}
+ // TODO
+ void visit (AST::TraitItemConst &) override { gcc_unreachable (); }
+
+ void visit (AST::TraitItemType &) override { gcc_unreachable (); }
+
private:
ResolveItem () : ResolverBase (UNKNOWN_NODEID) {}
};
diff --git a/gcc/rust/resolve/rust-ast-resolve-type.h b/gcc/rust/resolve/rust-ast-resolve-type.h
index bd990f7..19e7324 100644
--- a/gcc/rust/resolve/rust-ast-resolve-type.h
+++ b/gcc/rust/resolve/rust-ast-resolve-type.h
@@ -312,9 +312,7 @@ public:
// if it has a type lets resolve it
if (param.has_type ())
- {
- ResolveType::go (param.get_type ().get (), param.get_node_id ());
- }
+ ResolveType::go (param.get_type ().get (), param.get_node_id ());
// for now lets focus on handling the basics: like struct<T> { a:T, ....}
resolver->get_type_scope ().insert (
@@ -328,11 +326,7 @@ public:
}
private:
- ResolveGenericParam (NodeId parent)
- : ResolverBase (parent),
-
- ok (false)
- {}
+ ResolveGenericParam (NodeId parent) : ResolverBase (parent), ok (false) {}
bool ok;
};
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h
index 2e358d3..6a9fe87 100644
--- a/gcc/rust/typecheck/rust-hir-trait-resolve.h
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h
@@ -33,9 +33,11 @@ class ResolveTraitItemToRef : public TypeCheckBase
using Rust::Resolver::TypeCheckBase::visit;
public:
- static TraitItemReference Resolve (HIR::TraitItem &item)
+ static TraitItemReference
+ Resolve (HIR::TraitItem &item, TyTy::BaseType *self,
+ std::vector<TyTy::SubstitutionParamMapping> substitutions)
{
- ResolveTraitItemToRef resolver;
+ ResolveTraitItemToRef resolver (self, substitutions);
item.accept_vis (resolver);
return resolver.resolved;
}
@@ -89,7 +91,6 @@ public:
if (!context->lookup_type (fn.get_mappings ().get_hirid (), &ty))
{
HIR::TraitFunctionDecl &function = fn.get_decl ();
- std::vector<TyTy::SubstitutionParamMapping> substitutions;
if (function.has_generics ())
{
for (auto &generic_param : function.get_generic_params ())
@@ -135,6 +136,21 @@ public:
}
std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+ if (function.is_method ())
+ {
+ // add the synthetic self param at the front, this is a placeholder
+ // for compilation to know parameter names. The types are ignored
+ // but we reuse the HIR identifier pattern which requires it
+ HIR::SelfParam &self_param = function.get_self ();
+ HIR::IdentifierPattern *self_pattern = new HIR::IdentifierPattern (
+ "self", self_param.get_locus (), self_param.is_ref (),
+ self_param.is_mut (), std::unique_ptr<HIR::Pattern> (nullptr));
+ context->insert_type (self_param.get_mappings (), self->clone ());
+ params.push_back (
+ std::pair<HIR::Pattern *, TyTy::BaseType *> (self_pattern,
+ self->clone ()));
+ }
+
for (auto &param : function.get_function_params ())
{
// get the name as well required for later on
@@ -163,11 +179,16 @@ public:
}
private:
- ResolveTraitItemToRef ()
- : TypeCheckBase (), resolved (TraitItemReference::error ())
+ ResolveTraitItemToRef (
+ TyTy::BaseType *self,
+ std::vector<TyTy::SubstitutionParamMapping> substitutions)
+ : TypeCheckBase (), resolved (TraitItemReference::error ()), self (self),
+ substitutions (substitutions)
{}
TraitItemReference resolved;
+ TyTy::BaseType *self;
+ std::vector<TyTy::SubstitutionParamMapping> substitutions;
};
class TraitResolver : public TypeCheckBase
@@ -224,11 +245,41 @@ private:
return tref;
}
+ TyTy::BaseType *self = nullptr;
+ std::vector<TyTy::SubstitutionParamMapping> substitutions;
+ for (auto &generic_param : trait_reference->get_generic_params ())
+ {
+ switch (generic_param.get ()->get_kind ())
+ {
+ case HIR::GenericParam::GenericKind::LIFETIME:
+ // Skipping Lifetime completely until better handling.
+ break;
+
+ case HIR::GenericParam::GenericKind::TYPE: {
+ auto param_type
+ = TypeResolveGenericParam::Resolve (generic_param.get ());
+ context->insert_type (generic_param->get_mappings (), param_type);
+
+ auto &typaram = static_cast<HIR::TypeParam &> (*generic_param);
+ substitutions.push_back (
+ TyTy::SubstitutionParamMapping (typaram, param_type));
+
+ if (typaram.get_type_representation ().compare ("Self") == 0)
+ {
+ self = param_type;
+ }
+ }
+ break;
+ }
+ }
+
+ rust_assert (self != nullptr);
+
std::vector<TraitItemReference> item_refs;
for (auto &item : trait_reference->get_trait_items ())
{
TraitItemReference trait_item_ref
- = ResolveTraitItemToRef::Resolve (*item.get ());
+ = ResolveTraitItemToRef::Resolve (*item.get (), self, substitutions);
item_refs.push_back (std::move (trait_item_ref));
}
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
index 75ba095..242f6d7 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-implitem.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
@@ -303,7 +303,75 @@ public:
void visit (HIR::TypeAlias &type) override { gcc_unreachable (); }
- void visit (HIR::Method &method) override { gcc_unreachable (); }
+ void visit (HIR::Method &method) override
+ {
+ TypeCheckImplItem::visit (method);
+
+ // we get the error checking from the base method here
+ TyTy::BaseType *lookup;
+ if (!context->lookup_type (method.get_mappings ().get_hirid (), &lookup))
+ return;
+
+ if (lookup->get_kind () != TyTy::TypeKind::FNDEF)
+ return;
+
+ TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+ const TraitItemReference &trait_item_ref
+ = trait_reference.lookup_trait_item (
+ fntype->get_identifier (), TraitItemReference::TraitItemType::FN);
+
+ // unknown trait item
+ if (trait_item_ref.is_error ())
+ {
+ RichLocation r (method.get_locus ());
+ r.add_range (trait_reference.get_locus ());
+ rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>",
+ fntype->get_identifier ().c_str (),
+ trait_reference.get_name ().c_str ());
+ return;
+ }
+
+ rust_assert (trait_item_ref.get_tyty ()->get_kind ()
+ == TyTy::TypeKind::FNDEF);
+ TyTy::FnType *trait_item_fntype
+ = static_cast<TyTy::FnType *> (trait_item_ref.get_tyty ());
+
+ // sets substitute self into the trait_item_ref->tyty
+ TyTy::SubstitutionParamMapping *self_mapping = nullptr;
+ for (auto &param_mapping : trait_item_fntype->get_substs ())
+ {
+ const HIR::TypeParam &type_param = param_mapping.get_generic_param ();
+ if (type_param.get_type_representation ().compare ("Self") == 0)
+ {
+ self_mapping = &param_mapping;
+ break;
+ }
+ }
+ rust_assert (self_mapping != nullptr);
+
+ std::vector<TyTy::SubstitutionArg> mappings;
+ mappings.push_back (TyTy::SubstitutionArg (self_mapping, self));
+
+ TyTy::SubstitutionArgumentMappings implicit_self_substs (
+ mappings, method.get_locus ());
+ trait_item_fntype
+ = trait_item_fntype->handle_substitions (implicit_self_substs);
+
+ // check the types are compatible
+ if (!trait_item_fntype->can_eq (fntype))
+ {
+ RichLocation r (method.get_locus ());
+ r.add_range (trait_item_ref.get_locus ());
+
+ rust_error_at (
+ r, "method %<%s%> has an incompatible type for trait %<%s%>",
+ fntype->get_identifier ().c_str (),
+ trait_reference.get_name ().c_str ());
+ return;
+ }
+
+ resolved_trait_item = trait_item_ref;
+ }
void visit (HIR::Function &function) override
{
@@ -333,8 +401,34 @@ public:
return;
}
+ rust_assert (trait_item_ref.get_tyty ()->get_kind ()
+ == TyTy::TypeKind::FNDEF);
+ TyTy::FnType *trait_item_fntype
+ = static_cast<TyTy::FnType *> (trait_item_ref.get_tyty ());
+
+ // sets substitute self into the trait_item_ref->tyty
+ TyTy::SubstitutionParamMapping *self_mapping = nullptr;
+ for (auto &param_mapping : trait_item_fntype->get_substs ())
+ {
+ const HIR::TypeParam &type_param = param_mapping.get_generic_param ();
+ if (type_param.get_type_representation ().compare ("Self") == 0)
+ {
+ self_mapping = &param_mapping;
+ break;
+ }
+ }
+ rust_assert (self_mapping != nullptr);
+
+ std::vector<TyTy::SubstitutionArg> mappings;
+ mappings.push_back (TyTy::SubstitutionArg (self_mapping, self));
+
+ TyTy::SubstitutionArgumentMappings implicit_self_substs (
+ mappings, function.get_locus ());
+ trait_item_fntype
+ = trait_item_fntype->handle_substitions (implicit_self_substs);
+
// check the types are compatible
- if (!trait_item_ref.get_tyty ()->can_eq (fntype))
+ if (!trait_item_fntype->can_eq (fntype))
{
RichLocation r (function.get_locus ());
r.add_range (trait_item_ref.get_locus ());
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 51ede17..438c52b 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -468,7 +468,6 @@ class SubstitutionParamMapping
{
public:
SubstitutionParamMapping (const HIR::TypeParam &generic, ParamType *param)
-
: generic (generic), param (param)
{}
diff --git a/gcc/testsuite/rust/compile/torture/traits3.rs b/gcc/testsuite/rust/compile/torture/traits3.rs
new file mode 100644
index 0000000..621fcde
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits3.rs
@@ -0,0 +1,18 @@
+pub trait Foo {
+ fn Bar(self) -> i32;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .Bar." "" { target *-*-* } .-2 }
+}
+
+struct Baz;
+// { dg-warning "struct is never constructed: .Baz." "" { target *-*-* } .-1 }
+
+impl Foo for Baz {
+ fn Bar(self) -> i32 {
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .<Baz as Foo>::Bar." "" { target *-*-* } .-2 }
+ 123
+ }
+}
+
+fn main() {}