From 9411c061aaeae0be39c678e0885472c37b112141 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Mon, 14 Mar 2022 17:35:57 +0000 Subject: [PATCH 1/5] Refactor TypeBoundPredicate to be below the definition for SubstitutionRef This means TypeBoundPredicate will now be able to inherit all behaviours of normal generics so we do not duplicate the work in handling generics it will also allow us to more easily check for unconstrained type parameters on traits. --- gcc/rust/typecheck/rust-tyty-bounds.cc | 54 +++++++++++ gcc/rust/typecheck/rust-tyty.h | 124 ++++++++++--------------- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc index 1d1c481ce735..bc1e76ef69f6 100644 --- a/gcc/rust/typecheck/rust-tyty-bounds.cc +++ b/gcc/rust/typecheck/rust-tyty-bounds.cc @@ -67,6 +67,10 @@ TypeCheckBase::resolve_trait_path (HIR::TypePath &path) namespace TyTy { +TypeBoundPredicate::TypeBoundPredicate (DefId reference, Location locus) + : reference (reference), locus (locus), args (nullptr) +{} + std::string TypeBoundPredicate::as_string () const { @@ -192,5 +196,55 @@ TypeBoundPredicateItem::needs_implementation () const return !get_raw_item ()->is_optional (); } +// TypeBoundsMappings + +TypeBoundsMappings::TypeBoundsMappings ( + std::vector specified_bounds) + : specified_bounds (specified_bounds) +{} + +std::vector & +TypeBoundsMappings::get_specified_bounds () +{ + return specified_bounds; +} + +const std::vector & +TypeBoundsMappings::get_specified_bounds () const +{ + return specified_bounds; +} + +size_t +TypeBoundsMappings::num_specified_bounds () const +{ + return specified_bounds.size (); +} + +std::string +TypeBoundsMappings::raw_bounds_as_string () const +{ + std::string buf; + for (size_t i = 0; i < specified_bounds.size (); i++) + { + const TypeBoundPredicate &b = specified_bounds.at (i); + bool has_next = (i + 1) < specified_bounds.size (); + buf += b.get_name () + (has_next ? " + " : ""); + } + return buf; +} + +std::string +TypeBoundsMappings::bounds_as_string () const +{ + return "bounds:[" + raw_bounds_as_string () + "]"; +} + +void +TypeBoundsMappings::add_bound (TypeBoundPredicate predicate) +{ + specified_bounds.push_back (predicate); +} + } // namespace TyTy } // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index 82262abab092..d1c2170ba340 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -108,92 +108,24 @@ class TypeBoundPredicateItem const Resolver::TraitItemReference *trait_item_ref; }; -class TypeBoundPredicate -{ -public: - TypeBoundPredicate (DefId reference, Location locus) - : reference (reference), locus (locus), args (nullptr) - {} - - std::string as_string () const; - - const Resolver::TraitReference *get () const; - - Location get_locus () const { return locus; } - - std::string get_name () const; - - // check that this predicate is object-safe see: - // https://doc.rust-lang.org/reference/items/traits.html#object-safety - bool is_object_safe (bool emit_error, Location locus) const; - - void apply_generic_arguments (HIR::GenericArgs *generic_args); - - bool contains_item (const std::string &search) const; - - TypeBoundPredicateItem - lookup_associated_item (const std::string &search) const; - - HIR::GenericArgs *get_generic_args () { return args; } - - const HIR::GenericArgs *get_generic_args () const { return args; } - - bool has_generic_args () const - { - if (args == nullptr) - return false; - - return args->has_generic_args (); - } - -private: - DefId reference; - Location locus; - HIR::GenericArgs *args; -}; - class TypeBoundsMappings { protected: - TypeBoundsMappings (std::vector specified_bounds) - : specified_bounds (specified_bounds) - {} + TypeBoundsMappings (std::vector specified_bounds); public: - std::vector &get_specified_bounds () - { - return specified_bounds; - } + std::vector &get_specified_bounds (); - const std::vector &get_specified_bounds () const - { - return specified_bounds; - } + const std::vector &get_specified_bounds () const; - size_t num_specified_bounds () const { return specified_bounds.size (); } + size_t num_specified_bounds () const; - std::string raw_bounds_as_string () const - { - std::string buf; - for (size_t i = 0; i < specified_bounds.size (); i++) - { - const TypeBoundPredicate &b = specified_bounds.at (i); - bool has_next = (i + 1) < specified_bounds.size (); - buf += b.get_name () + (has_next ? " + " : ""); - } - return buf; - } + std::string raw_bounds_as_string () const; - std::string bounds_as_string () const - { - return "bounds:[" + raw_bounds_as_string () + "]"; - } + std::string bounds_as_string () const; protected: - void add_bound (TypeBoundPredicate predicate) - { - specified_bounds.push_back (predicate); - } + void add_bound (TypeBoundPredicate predicate); std::vector specified_bounds; }; @@ -1007,6 +939,48 @@ class SubstitutionRef SubstitutionArgumentMappings used_arguments; }; +class TypeBoundPredicate +{ +public: + TypeBoundPredicate (DefId reference, Location locus); + + std::string as_string () const; + + const Resolver::TraitReference *get () const; + + Location get_locus () const { return locus; } + + std::string get_name () const; + + // check that this predicate is object-safe see: + // https://doc.rust-lang.org/reference/items/traits.html#object-safety + bool is_object_safe (bool emit_error, Location locus) const; + + void apply_generic_arguments (HIR::GenericArgs *generic_args); + + bool contains_item (const std::string &search) const; + + TypeBoundPredicateItem + lookup_associated_item (const std::string &search) const; + + HIR::GenericArgs *get_generic_args () { return args; } + + const HIR::GenericArgs *get_generic_args () const { return args; } + + bool has_generic_args () const + { + if (args == nullptr) + return false; + + return args->has_generic_args (); + } + +private: + DefId reference; + Location locus; + HIR::GenericArgs *args; +}; + // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.VariantDef.html class VariantDef { From 56a1571614ab807bfc12a7a43ae4926b2fc1f744 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 16 Mar 2022 14:34:26 +0000 Subject: [PATCH 2/5] Keep track of substitution mappings as part of the TraitReference The TraitReference wrapper is a class that allows us to work with traits in a query manar. This patch allows us to keep track of the substitution mappings that are defined on the trait. This will always be non-empty since traits always contain an implicit Self type parameter so there is special handling in how we perform monomorphization. --- gcc/rust/typecheck/rust-hir-trait-ref.h | 39 ++++++++++++++++++--- gcc/rust/typecheck/rust-hir-trait-resolve.h | 3 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index 613ed711c438..4ad0d7b67e8c 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -181,19 +181,37 @@ class TraitReference public: TraitReference (const HIR::Trait *hir_trait_ref, std::vector item_refs, - std::vector super_traits) + std::vector super_traits, + std::vector substs) : hir_trait_ref (hir_trait_ref), item_refs (item_refs), super_traits (super_traits) - {} + { + trait_substs.clear (); + trait_substs.reserve (substs.size ()); + for (const auto &p : substs) + trait_substs.push_back (p.clone ()); + } TraitReference (TraitReference const &other) - : hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs) - {} + : hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs), + super_traits (other.super_traits) + { + trait_substs.clear (); + trait_substs.reserve (other.trait_substs.size ()); + for (const auto &p : other.trait_substs) + trait_substs.push_back (p.clone ()); + } TraitReference &operator= (TraitReference const &other) { hir_trait_ref = other.hir_trait_ref; item_refs = other.item_refs; + super_traits = other.super_traits; + + trait_substs.clear (); + trait_substs.reserve (other.trait_substs.size ()); + for (const auto &p : other.trait_substs) + trait_substs.push_back (p.clone ()); return *this; } @@ -201,7 +219,10 @@ class TraitReference TraitReference (TraitReference &&other) = default; TraitReference &operator= (TraitReference &&other) = default; - static TraitReference error () { return TraitReference (nullptr, {}, {}); } + static TraitReference error () + { + return TraitReference (nullptr, {}, {}, {}); + } bool is_error () const { return hir_trait_ref == nullptr; } @@ -384,10 +405,18 @@ class TraitReference return is_safe; } + bool trait_has_generics () const { return !trait_substs.empty (); } + + const std::vector &get_trait_substs () const + { + return trait_substs; + } + private: const HIR::Trait *hir_trait_ref; std::vector item_refs; std::vector super_traits; + std::vector trait_substs; }; class AssociatedImplTrait diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h index d8df481076c1..7af353a8c2ad 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.h +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -193,7 +193,8 @@ class TraitResolver : public TypeCheckBase } TraitReference trait_object (trait_reference, item_refs, - std::move (super_traits)); + std::move (super_traits), + std::move (substitutions)); context->insert_trait_reference ( trait_reference->get_mappings ().get_defid (), std::move (trait_object)); From 7ac9a76c8983df0d5a30b1c2fafbff58d7cb7e52 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 16 Mar 2022 15:22:52 +0000 Subject: [PATCH 3/5] Make TypeBoundPredicate a subclass of the SubstitutionRef This will allow us to reuse our generic substitions code to manage generic traits and their substitions better. It will unify the handling in one path so we get the same error handling. --- gcc/rust/typecheck/rust-hir-trait-ref.h | 2 +- gcc/rust/typecheck/rust-hir-trait-resolve.h | 9 +++- gcc/rust/typecheck/rust-hir-type-check-item.h | 4 +- .../typecheck/rust-hir-type-check-type.cc | 3 +- gcc/rust/typecheck/rust-hir-type-check-type.h | 6 +-- gcc/rust/typecheck/rust-tyty-bounds.cc | 52 ++++++++++++++++--- gcc/rust/typecheck/rust-tyty.h | 18 ++++++- 7 files changed, 75 insertions(+), 19 deletions(-) diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index 4ad0d7b67e8c..585fb6c8dbfe 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -407,7 +407,7 @@ class TraitReference bool trait_has_generics () const { return !trait_substs.empty (); } - const std::vector &get_trait_substs () const + std::vector get_trait_substs () const { return trait_substs; } diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h index 7af353a8c2ad..806a46f13cee 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.h +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -150,8 +150,13 @@ class TraitResolver : public TypeCheckBase // They also inherit themselves as a bound this enables a trait item to // reference other Self::trait_items + std::vector self_subst_copy; + for (auto &sub : substitutions) + self_subst_copy.push_back (sub.clone ()); + specified_bounds.push_back ( TyTy::TypeBoundPredicate (trait_reference->get_mappings ().get_defid (), + std::move (self_subst_copy), trait_reference->get_locus ())); std::vector super_traits; @@ -168,8 +173,8 @@ class TraitResolver : public TypeCheckBase // FIXME this might be recursive we need a check for that TraitReference *trait = resolve_trait_path (b->get_path ()); - TyTy::TypeBoundPredicate predicate ( - trait->get_mappings ().get_defid (), bound->get_locus ()); + TyTy::TypeBoundPredicate predicate (*trait, + bound->get_locus ()); specified_bounds.push_back (std::move (predicate)); super_traits.push_back (predicate.get ()); diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h index f86e967bcf49..8a41548babb9 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.h +++ b/gcc/rust/typecheck/rust-hir-type-check-item.h @@ -79,8 +79,8 @@ class TypeCheckItem : public TypeCheckBase rust_assert (!trait_reference->is_error ()); // setup the bound - TyTy::TypeBoundPredicate predicate ( - trait_reference->get_mappings ().get_defid (), ref->get_locus ()); + TyTy::TypeBoundPredicate predicate (*trait_reference, + ref->get_locus ()); auto &final_seg = ref->get_final_segment (); if (final_seg->is_generic_segment ()) { diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc index 6c6546babe6e..ee77128143df 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc @@ -550,8 +550,7 @@ TypeCheckType::visit (HIR::TraitObjectType &type) auto &type_path = trait_bound.get_path (); TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate (trait->get_mappings ().get_defid (), - trait_bound.get_locus ()); + TyTy::TypeBoundPredicate predicate (*trait, trait_bound.get_locus ()); auto &final_seg = type_path.get_final_segment (); if (final_seg->is_generic_segment ()) { diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h index e03756237ac5..373287e113e4 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.h +++ b/gcc/rust/typecheck/rust-hir-type-check-type.h @@ -247,8 +247,7 @@ class TypeResolveGenericParam : public TypeCheckBase auto &type_path = b->get_path (); TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate ( - trait->get_mappings ().get_defid (), b->get_locus ()); + TyTy::TypeBoundPredicate predicate (*trait, b->get_locus ()); auto &final_seg = type_path.get_final_segment (); if (final_seg->is_generic_segment ()) @@ -318,8 +317,7 @@ class ResolveWhereClauseItem : public TypeCheckBase auto &type_path = b->get_path (); TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate ( - trait->get_mappings ().get_defid (), b->get_locus ()); + TyTy::TypeBoundPredicate predicate (*trait, b->get_locus ()); auto &final_seg = type_path.get_final_segment (); if (final_seg->is_generic_segment ()) diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc index bc1e76ef69f6..25d4966aa633 100644 --- a/gcc/rust/typecheck/rust-tyty-bounds.cc +++ b/gcc/rust/typecheck/rust-tyty-bounds.cc @@ -67,17 +67,38 @@ TypeCheckBase::resolve_trait_path (HIR::TypePath &path) namespace TyTy { -TypeBoundPredicate::TypeBoundPredicate (DefId reference, Location locus) - : reference (reference), locus (locus), args (nullptr) +TypeBoundPredicate::TypeBoundPredicate ( + const Resolver::TraitReference &trait_reference, Location locus) + : SubstitutionRef (trait_reference.get_trait_substs (), + SubstitutionArgumentMappings::error ()), + reference (trait_reference.get_mappings ().get_defid ()), locus (locus), + args (nullptr), error_flag (false) {} +TypeBoundPredicate::TypeBoundPredicate ( + DefId reference, std::vector substitutions, + Location locus) + : SubstitutionRef (std::move (substitutions), + SubstitutionArgumentMappings::error ()), + reference (reference), locus (locus), args (nullptr), error_flag (false) +{} + +TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other) + : SubstitutionRef ({}, other.used_arguments), reference (other.reference), + locus (other.locus), args (other.args), error_flag (other.error_flag) +{ + substitutions.clear (); + if (!other.is_error ()) + { + for (const auto &p : other.get_substs ()) + substitutions.push_back (p.clone ()); + } +} + std::string TypeBoundPredicate::as_string () const { - return get ()->as_string () - + (has_generic_args () - ? std::string ("<") + args->as_string () + std::string (">") - : ""); + return get ()->as_string () + subst_as_string (); } const Resolver::TraitReference * @@ -183,6 +204,25 @@ TypeBoundPredicateItem::get_tyty_for_receiver ( return resolved; } +bool +TypeBoundPredicate::is_error () const +{ + auto context = Resolver::TypeCheckContext::get (); + + Resolver::TraitReference *ref = nullptr; + bool ok = context->lookup_trait_reference (reference, &ref); + + return !ok || error_flag; +} + +BaseType * +TypeBoundPredicate::handle_substitions (SubstitutionArgumentMappings mappings) +{ + gcc_unreachable (); + return nullptr; +} + +// trait item reference const Resolver::TraitItemReference * TypeBoundPredicateItem::get_raw_item () const diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index d1c2170ba340..44eb81f43dbe 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -939,10 +939,17 @@ class SubstitutionRef SubstitutionArgumentMappings used_arguments; }; -class TypeBoundPredicate +class TypeBoundPredicate : public SubstitutionRef { public: - TypeBoundPredicate (DefId reference, Location locus); + TypeBoundPredicate (const Resolver::TraitReference &trait_reference, + Location locus); + + TypeBoundPredicate (DefId reference, + std::vector substitutions, + Location locus); + + TypeBoundPredicate (const TypeBoundPredicate &other); std::string as_string () const; @@ -975,10 +982,17 @@ class TypeBoundPredicate return args->has_generic_args (); } + // WARNING THIS WILL ALWAYS RETURN NULLPTR + BaseType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + + bool is_error () const; + private: DefId reference; Location locus; HIR::GenericArgs *args; + bool error_flag; }; // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.VariantDef.html From 21cf0e67a6f748525ca79e8d9aa59c94bef13519 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 16 Mar 2022 15:49:26 +0000 Subject: [PATCH 4/5] Add missing location info on GenericArgs --- gcc/rust/hir/tree/rust-hir-path.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gcc/rust/hir/tree/rust-hir-path.h b/gcc/rust/hir/tree/rust-hir-path.h index 9858ba393d86..99f4f0b02c54 100644 --- a/gcc/rust/hir/tree/rust-hir-path.h +++ b/gcc/rust/hir/tree/rust-hir-path.h @@ -133,8 +133,7 @@ struct GenericArgs GenericArgs (std::vector lifetime_args, std::vector > type_args, - std::vector binding_args, - Location locus = Location ()) + std::vector binding_args, Location locus) : lifetime_args (std::move (lifetime_args)), type_args (std::move (type_args)), binding_args (std::move (binding_args)), locus (locus) @@ -471,7 +470,7 @@ class TypePathSegmentGeneric : public TypePathSegment has_separating_scope_resolution, locus), generic_args (GenericArgs (std::move (lifetime_args), std::move (type_args), - std::move (binding_args))) + std::move (binding_args), locus)) {} std::string as_string () const override; From 8086790254d0adaad48405ee838b8ca699e9d9a8 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 16 Mar 2022 16:19:31 +0000 Subject: [PATCH 5/5] Fix unconstrained type parameter checks This patch removes our old method of checking for unconstrained type parameters which only worked for the basic cases such as: impl Foo { } But checking for unconstrained types is more complex, we need to handle covariant types such as: impl *T { } Or struct foo(X,Y); impl foo<&T,*T> {} This rewrites the algorithm to take advantage of our substition abstractions and HirIds so we can map the ids of the type-params to be constrained and look at the trait-references used-arguments when the generics are applied (or they may be empty) and then do the same for any used arguments on an algebraic data type. Fixes #1019 --- gcc/rust/Make-lang.in | 1 + .../typecheck/rust-hir-type-check-base.cc | 84 ++++++++++++++++ gcc/rust/typecheck/rust-hir-type-check-base.h | 8 ++ gcc/rust/typecheck/rust-hir-type-check-item.h | 43 ++++---- .../typecheck/rust-hir-type-check-toplevel.h | 5 +- .../typecheck/rust-hir-type-check-type.cc | 28 +----- gcc/rust/typecheck/rust-hir-type-check-type.h | 97 +++---------------- gcc/rust/typecheck/rust-tyty-bounds.cc | 87 ++++++++++++++++- gcc/rust/typecheck/rust-tyty.cc | 17 ++-- gcc/rust/typecheck/rust-tyty.h | 39 +++++--- gcc/testsuite/rust/compile/issue-1019.rs | 19 ++++ gcc/testsuite/rust/compile/traits12.rs | 20 ++++ 12 files changed, 289 insertions(+), 159 deletions(-) create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.cc create mode 100644 gcc/testsuite/rust/compile/issue-1019.rs create mode 100644 gcc/testsuite/rust/compile/traits12.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 76a45e7c8eed..cd40efb9d339 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -99,6 +99,7 @@ GRS_OBJS = \ rust/rust-hir-type-check-pattern.o \ rust/rust-hir-type-check-expr.o \ rust/rust-hir-dot-operator.o \ + rust/rust-hir-type-check-base.o \ rust/rust-autoderef.o \ rust/rust-substitution-mapper.o \ rust/rust-lint-marklive.o \ diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc new file mode 100644 index 000000000000..32c588165826 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc @@ -0,0 +1,84 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-base.h" + +namespace Rust { +namespace Resolver { + +bool +TypeCheckBase::check_for_unconstrained ( + const std::vector ¶ms_to_constrain, + const TyTy::SubstitutionArgumentMappings &constraint_a, + const TyTy::SubstitutionArgumentMappings &constraint_b, + const TyTy::BaseType *reference) +{ + std::set symbols_to_constrain; + std::map symbol_to_location; + for (const auto &p : params_to_constrain) + { + HirId ref = p.get_param_ty ()->get_ref (); + symbols_to_constrain.insert (ref); + symbol_to_location.insert ({ref, p.get_param_locus ()}); + } + + // set up the set of constrained symbols + std::set constrained_symbols; + for (const auto &c : constraint_a.get_mappings ()) + { + const TyTy::BaseType *arg = c.get_tyty (); + if (arg != nullptr) + { + const TyTy::BaseType *p = arg->get_root (); + constrained_symbols.insert (p->get_ty_ref ()); + } + } + for (const auto &c : constraint_b.get_mappings ()) + { + const TyTy::BaseType *arg = c.get_tyty (); + if (arg != nullptr) + { + const TyTy::BaseType *p = arg->get_root (); + constrained_symbols.insert (p->get_ty_ref ()); + } + } + + const auto root = reference->get_root (); + if (root->get_kind () == TyTy::TypeKind::PARAM) + { + const TyTy::ParamType *p = static_cast (root); + constrained_symbols.insert (p->get_ty_ref ()); + } + + // check for unconstrained + bool unconstrained = false; + for (auto &sym : symbols_to_constrain) + { + bool used = constrained_symbols.find (sym) != constrained_symbols.end (); + if (!used) + { + Location locus = symbol_to_location.at (sym); + rust_error_at (locus, "unconstrained type parameter"); + unconstrained = true; + } + } + return unconstrained; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h index 830b95ca4901..5a3f553367f3 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-base.h +++ b/gcc/rust/typecheck/rust-hir-type-check-base.h @@ -47,6 +47,14 @@ class TypeCheckBase : public HIR::HIRFullVisitorBase TraitReference *resolve_trait_path (HIR::TypePath &); + TyTy::TypeBoundPredicate get_predicate_from_bound (HIR::TypePath &path); + + bool check_for_unconstrained ( + const std::vector ¶ms_to_constrain, + const TyTy::SubstitutionArgumentMappings &constraint_a, + const TyTy::SubstitutionArgumentMappings &constraint_b, + const TyTy::BaseType *reference); + Analysis::Mappings *mappings; Resolver *resolver; TypeCheckContext *context; diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h index 8a41548babb9..4c6c4a633d29 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.h +++ b/gcc/rust/typecheck/rust-hir-type-check-item.h @@ -70,7 +70,7 @@ class TypeCheckItem : public TypeCheckBase } } - std::vector specified_bounds; + auto specified_bound = TyTy::TypeBoundPredicate::error (); TraitReference *trait_reference = &TraitReference::error_node (); if (impl_block.has_trait_ref ()) { @@ -78,26 +78,7 @@ class TypeCheckItem : public TypeCheckBase trait_reference = TraitResolver::Resolve (*ref.get ()); rust_assert (!trait_reference->is_error ()); - // setup the bound - TyTy::TypeBoundPredicate predicate (*trait_reference, - ref->get_locus ()); - auto &final_seg = ref->get_final_segment (); - if (final_seg->is_generic_segment ()) - { - auto final_generic_seg - = static_cast (final_seg.get ()); - if (final_generic_seg->has_generic_args ()) - { - HIR::GenericArgs &generic_args - = final_generic_seg->get_generic_args (); - - // this is applying generic arguments to a trait - // reference - predicate.apply_generic_arguments (&generic_args); - } - } - - specified_bounds.push_back (std::move (predicate)); + specified_bound = get_predicate_from_bound (*ref.get ()); } TyTy::BaseType *self = nullptr; @@ -108,11 +89,25 @@ class TypeCheckItem : public TypeCheckBase "failed to resolve Self for ImplBlock"); return; } - // inherit the bounds - self->inherit_bounds (specified_bounds); + // inherit the bounds + if (!specified_bound.is_error ()) + self->inherit_bounds ({specified_bound}); + + // check for any unconstrained type-params + const TyTy::SubstitutionArgumentMappings trait_constraints + = specified_bound.get_substitution_arguments (); + const TyTy::SubstitutionArgumentMappings impl_constraints + = GetUsedSubstArgs::From (self); + + bool impl_block_has_unconstrained_typarams + = check_for_unconstrained (substitutions, trait_constraints, + impl_constraints, self); + if (impl_block_has_unconstrained_typarams) + return; + + // validate the impl items bool is_trait_impl_block = !trait_reference->is_error (); - std::vector trait_item_refs; for (auto &impl_item : impl_block.get_impl_items ()) { diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h index 2271dace78ca..5e124663fe74 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h +++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h @@ -468,9 +468,8 @@ class TypeCheckTopLevel : public TypeCheckBase ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); } - auto self - = TypeCheckType::Resolve (impl_block.get_type ().get (), &substitutions); - if (self == nullptr || self->get_kind () == TyTy::TypeKind::ERROR) + auto self = TypeCheckType::Resolve (impl_block.get_type ().get ()); + if (self->get_kind () == TyTy::TypeKind::ERROR) return; for (auto &impl_item : impl_block.get_impl_items ()) diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc index ee77128143df..914bebbecf82 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc @@ -84,11 +84,6 @@ TypeCheckType::visit (HIR::TypePath &path) } translated = SubstMapper::Resolve (path_type, path.get_locus (), &args); - if (translated->get_kind () != TyTy::TypeKind::ERROR - && mappings != nullptr) - { - check_for_unconstrained (args.get_type_args ()); - } } else if (!args.is_empty ()) { @@ -548,26 +543,11 @@ TypeCheckType::visit (HIR::TraitObjectType &type) HIR::TypeParamBound &b = *bound.get (); HIR::TraitBound &trait_bound = static_cast (b); - auto &type_path = trait_bound.get_path (); - TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate (*trait, trait_bound.get_locus ()); - auto &final_seg = type_path.get_final_segment (); - if (final_seg->is_generic_segment ()) - { - auto final_generic_seg - = static_cast (final_seg.get ()); - if (final_generic_seg->has_generic_args ()) - { - HIR::GenericArgs &generic_args - = final_generic_seg->get_generic_args (); - - // this is applying generic arguments to a trait - // reference - predicate.apply_generic_arguments (&generic_args); - } - } + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (trait_bound.get_path ()); - if (predicate.is_object_safe (true, type.get_locus ())) + if (!predicate.is_error () + && predicate.is_object_safe (true, type.get_locus ())) specified_bounds.push_back (std::move (predicate)); } diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h index 373287e113e4..127502f14941 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.h +++ b/gcc/rust/typecheck/rust-hir-type-check-type.h @@ -57,12 +57,9 @@ class TypeCheckType : public TypeCheckBase using Rust::Resolver::TypeCheckBase::visit; public: - static TyTy::BaseType * - Resolve (HIR::Type *type, - std::vector *subst_mappings - = nullptr) + static TyTy::BaseType *Resolve (HIR::Type *type) { - TypeCheckType resolver (type->get_mappings ().get_hirid (), subst_mappings); + TypeCheckType resolver (type->get_mappings ().get_hirid ()); type->accept_vis (resolver); rust_assert (resolver.translated != nullptr); resolver.context->insert_type (type->get_mappings (), resolver.translated); @@ -159,43 +156,10 @@ class TypeCheckType : public TypeCheckBase void visit (HIR::TraitObjectType &type) override; private: - TypeCheckType (HirId id, - std::vector *subst_mappings) - : TypeCheckBase (), subst_mappings (subst_mappings), - translated (new TyTy::ErrorType (id)) + TypeCheckType (HirId id) + : TypeCheckBase (), translated (new TyTy::ErrorType (id)) {} - void - check_for_unconstrained (std::vector> &type_args) - { - std::map param_location_map; - std::set param_tys; - - if (subst_mappings != nullptr) - { - for (auto &mapping : *subst_mappings) - { - std::string sym = mapping.get_param_ty ()->get_symbol (); - param_tys.insert (sym); - param_location_map[sym] = mapping.get_generic_param ().get_locus (); - } - } - - std::set args; - for (auto &arg : type_args) - args.insert (arg->as_string ()); - - for (auto &exp : param_tys) - { - bool used = args.find (exp) != args.end (); - if (!used) - { - Location locus = param_location_map.at (exp); - rust_error_at (locus, "unconstrained type parameter"); - } - } - } - TyTy::BaseType *resolve_root_path (HIR::TypePath &path, size_t *offset, NodeId *root_resolved_node_id); @@ -205,7 +169,6 @@ class TypeCheckType : public TypeCheckBase TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings, Location expr_locus); - std::vector *subst_mappings; TyTy::BaseType *translated; }; @@ -245,28 +208,10 @@ class TypeResolveGenericParam : public TypeCheckBase HIR::TraitBound *b = static_cast (bound.get ()); - auto &type_path = b->get_path (); - TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate (*trait, b->get_locus ()); - - auto &final_seg = type_path.get_final_segment (); - if (final_seg->is_generic_segment ()) - { - auto final_generic_seg - = static_cast ( - final_seg.get ()); - if (final_generic_seg->has_generic_args ()) - { - HIR::GenericArgs &generic_args - = final_generic_seg->get_generic_args (); - - // this is applying generic arguments to a trait - // reference - predicate.apply_generic_arguments (&generic_args); - } - } - - specified_bounds.push_back (std::move (predicate)); + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (b->get_path ()); + if (!predicate.is_error ()) + specified_bounds.push_back (std::move (predicate)); } break; @@ -315,28 +260,10 @@ class ResolveWhereClauseItem : public TypeCheckBase HIR::TraitBound *b = static_cast (bound.get ()); - auto &type_path = b->get_path (); - TraitReference *trait = resolve_trait_path (type_path); - TyTy::TypeBoundPredicate predicate (*trait, b->get_locus ()); - - auto &final_seg = type_path.get_final_segment (); - if (final_seg->is_generic_segment ()) - { - auto final_generic_seg - = static_cast ( - final_seg.get ()); - if (final_generic_seg->has_generic_args ()) - { - HIR::GenericArgs &generic_args - = final_generic_seg->get_generic_args (); - - // this is applying generic arguments to a trait - // reference - predicate.apply_generic_arguments (&generic_args); - } - } - - specified_bounds.push_back (std::move (predicate)); + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (b->get_path ()); + if (!predicate.is_error ()) + specified_bounds.push_back (std::move (predicate)); } break; diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc index 25d4966aa633..a7ec42c7a1c2 100644 --- a/gcc/rust/typecheck/rust-tyty-bounds.cc +++ b/gcc/rust/typecheck/rust-tyty-bounds.cc @@ -63,6 +63,37 @@ TypeCheckBase::resolve_trait_path (HIR::TypePath &path) return TraitResolver::Resolve (path); } +TyTy::TypeBoundPredicate +TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path) +{ + TraitReference *trait = resolve_trait_path (type_path); + if (trait->is_error ()) + return TyTy::TypeBoundPredicate::error (); + + TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ()); + HIR::GenericArgs args + = HIR::GenericArgs::create_empty (type_path.get_locus ()); + + auto &final_seg = type_path.get_final_segment (); + if (final_seg->is_generic_segment ()) + { + auto final_generic_seg + = static_cast (final_seg.get ()); + if (final_generic_seg->has_generic_args ()) + { + args = final_generic_seg->get_generic_args (); + } + } + + if (predicate.requires_generic_args ()) + { + // this is applying generic arguments to a trait reference + predicate.apply_generic_arguments (&args); + } + + return predicate; +} + } // namespace Resolver namespace TyTy { @@ -72,7 +103,7 @@ TypeBoundPredicate::TypeBoundPredicate ( : SubstitutionRef (trait_reference.get_trait_substs (), SubstitutionArgumentMappings::error ()), reference (trait_reference.get_mappings ().get_defid ()), locus (locus), - args (nullptr), error_flag (false) + args (HIR::GenericArgs::create_empty ()), error_flag (false) {} TypeBoundPredicate::TypeBoundPredicate ( @@ -80,7 +111,8 @@ TypeBoundPredicate::TypeBoundPredicate ( Location locus) : SubstitutionRef (std::move (substitutions), SubstitutionArgumentMappings::error ()), - reference (reference), locus (locus), args (nullptr), error_flag (false) + reference (reference), locus (locus), + args (HIR::GenericArgs::create_empty ()), error_flag (false) {} TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other) @@ -95,6 +127,33 @@ TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other) } } +TypeBoundPredicate & +TypeBoundPredicate::operator= (const TypeBoundPredicate &other) +{ + reference = other.reference; + locus = other.locus; + args = other.args; + error_flag = other.error_flag; + used_arguments = other.used_arguments; + + substitutions.clear (); + if (!other.is_error ()) + { + for (const auto &p : other.get_substs ()) + substitutions.push_back (p.clone ()); + } + + return *this; +} + +TypeBoundPredicate +TypeBoundPredicate::error () +{ + auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ()); + p.error_flag = true; + return p; +} + std::string TypeBoundPredicate::as_string () const { @@ -139,8 +198,19 @@ TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const void TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args) { - args = generic_args; - // TODO verify these arguments are valid and not too many were added + // we need to get the substitutions argument mappings but also remember that + // we have an implicit Self argument which we must be careful to respect + rust_assert (used_arguments.is_empty ()); + rust_assert (!substitutions.empty ()); + + // we setup a dummy implict self argument + SubstitutionArg placeholder_self (&substitutions.front (), nullptr); + used_arguments.get_mappings ().push_back (std::move (placeholder_self)); + + // now actually perform a substitution + used_arguments = get_mappings_from_generic_args (*generic_args); + error_flag |= used_arguments.is_error (); + args = *generic_args; } bool @@ -222,6 +292,15 @@ TypeBoundPredicate::handle_substitions (SubstitutionArgumentMappings mappings) return nullptr; } +bool +TypeBoundPredicate::requires_generic_args () const +{ + if (is_error ()) + return false; + + return substitutions.size () > 1 && args.is_empty (); +} + // trait item reference const Resolver::TraitItemReference * diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc index 8d5cc5e511a2..c12095f26ebf 100644 --- a/gcc/rust/typecheck/rust-tyty.cc +++ b/gcc/rust/typecheck/rust-tyty.cc @@ -201,11 +201,16 @@ BaseType::inherit_bounds ( const BaseType * BaseType::get_root () const { - const BaseType *root = this; - while (root->get_kind () == TyTy::REF) + const TyTy::BaseType *root = this; + if (get_kind () == TyTy::REF) { const ReferenceType *r = static_cast (root); - root = r->get_base (); + root = r->get_base ()->get_root (); + } + else if (get_kind () == TyTy::POINTER) + { + const PointerType *r = static_cast (root); + root = r->get_base ()->get_root (); } return root; } @@ -543,11 +548,11 @@ SubstitutionRef::get_mappings_from_generic_args (HIR::GenericArgs &args) rust_error_at ( r, "generic item takes at least %lu type arguments but %lu were supplied", - substitutions.size (), args.get_type_args ().size ()); + (min_required_substitutions () - offs), args.get_type_args ().size ()); return SubstitutionArgumentMappings::error (); } - std::vector mappings; + std::vector mappings = used_arguments.get_mappings (); for (auto &arg : args.get_type_args ()) { BaseType *resolved = Resolver::TypeCheckType::Resolve (arg.get ()); @@ -2351,7 +2356,7 @@ ParamType::handle_substitions (SubstitutionArgumentMappings mappings) SubstitutionArg arg = SubstitutionArg::error (); bool ok = mappings.get_argument_for_symbol (this, &arg); - if (ok) + if (ok && !arg.is_error ()) p->set_ty_ref (arg.get_tyty ()->get_ref ()); return p; diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index 44eb81f43dbe..58b5042c34ab 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -551,7 +551,13 @@ class SubstitutionParamMapping : generic (other.generic), param (other.param) {} - std::string as_string () const { return param->get_name (); } + std::string as_string () const + { + if (param == nullptr) + return "nullptr"; + + return param->get_name (); + } bool fill_param_ty (BaseType &type, Location locus); @@ -615,7 +621,9 @@ class SubstitutionArg BaseType *get_tyty () { return argument; } - const SubstitutionParamMapping *get_param_mapping () { return param; } + const BaseType *get_tyty () const { return argument; } + + const SubstitutionParamMapping *get_param_mapping () const { return param; } static SubstitutionArg error () { return SubstitutionArg (nullptr, nullptr); } @@ -634,7 +642,8 @@ class SubstitutionArg std::string as_string () const { - return param->as_string () + ":" + argument->as_string (); + return param->as_string () + + (argument != nullptr ? ":" + argument->as_string () : ""); } private: @@ -712,8 +721,12 @@ class SubstitutionArgumentMappings size_t size () const { return mappings.size (); } + bool is_empty () const { return size () == 0; } + std::vector &get_mappings () { return mappings; } + const std::vector &get_mappings () const { return mappings; } + std::string as_string () const { std::string buffer; @@ -951,6 +964,10 @@ class TypeBoundPredicate : public SubstitutionRef TypeBoundPredicate (const TypeBoundPredicate &other); + TypeBoundPredicate &operator= (const TypeBoundPredicate &other); + + static TypeBoundPredicate error (); + std::string as_string () const; const Resolver::TraitReference *get () const; @@ -970,17 +987,11 @@ class TypeBoundPredicate : public SubstitutionRef TypeBoundPredicateItem lookup_associated_item (const std::string &search) const; - HIR::GenericArgs *get_generic_args () { return args; } + HIR::GenericArgs *get_generic_args () { return &args; } - const HIR::GenericArgs *get_generic_args () const { return args; } + const HIR::GenericArgs *get_generic_args () const { return &args; } - bool has_generic_args () const - { - if (args == nullptr) - return false; - - return args->has_generic_args (); - } + bool has_generic_args () const { return args.has_generic_args (); } // WARNING THIS WILL ALWAYS RETURN NULLPTR BaseType * @@ -988,10 +999,12 @@ class TypeBoundPredicate : public SubstitutionRef bool is_error () const; + bool requires_generic_args () const; + private: DefId reference; Location locus; - HIR::GenericArgs *args; + HIR::GenericArgs args; bool error_flag; }; diff --git a/gcc/testsuite/rust/compile/issue-1019.rs b/gcc/testsuite/rust/compile/issue-1019.rs new file mode 100644 index 000000000000..aea86a821c77 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-1019.rs @@ -0,0 +1,19 @@ +trait A { + type Output; + + fn test(self, a: &T) -> &Self::Output; +} + +struct Foo { + // { dg-warning "struct is never constructed" "" { target *-*-* } .-1 } + start: T, + end: T, +} + +impl A for Foo { + type Output = X; + + fn test(self, a: &X) -> &Self::Output { + a + } +} diff --git a/gcc/testsuite/rust/compile/traits12.rs b/gcc/testsuite/rust/compile/traits12.rs new file mode 100644 index 000000000000..25e0eb7aaa3c --- /dev/null +++ b/gcc/testsuite/rust/compile/traits12.rs @@ -0,0 +1,20 @@ +trait A { + type Output; + + fn test(self, a: &T) -> &Self::Output; +} + +struct Foo { + start: T, + end: T, +} + +impl A for Foo { + // { dg-error "generic item takes at least 1 type arguments but 0 were supplied" "" { target *-*-* } .-1 } + // { dg-error "unconstrained type parameter" "" { target *-*-* } .-2 } + type Output = T; + + fn test(self, a: &T) -> &Self::Output { + a + } +}