Skip to content

Improve auto traits #3407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-bounds.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef RUST_HIR_TYPE_BOUNDS_H
#define RUST_HIR_TYPE_BOUNDS_H

#include "rust-hir-trait-reference.h"
#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
#include "rust-tyty.h"
Expand All @@ -40,12 +41,30 @@ class TypeBoundsProbe : public TypeCheckBase
void assemble_sized_builtin ();
void add_trait_bound (HIR::Trait *trait);
void assemble_builtin_candidate (LangItem::Kind item);
bool should_impl_auto_trait (HIR::Trait *trait);
bool is_auto_trait_bound_satisfied (const TyTy::BaseType *raw,
HIR::Trait *trait);

private:
TypeBoundsProbe (const TyTy::BaseType *receiver);

const TyTy::BaseType *receiver;
std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> trait_references;

bool bound_cache_lookup (TyTy::BaseType *tyty, TraitReference *trait);
void bound_cache_insert (TyTy::BaseType *tyty, TraitReference *trait,
bool satisfied);

bool impl_cache_lookup (TyTy::BaseType *tyty);
void impl_cache_insert (TyTy::BaseType *tyty, bool satisfied);

// memoization cache (type)->(trait, impl)
static std::map<TyTy::BaseType *,
std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>>
trait_reference_cache;
// memoization cache (type, trait)->(bool)
static std::map<std::pair<TyTy::BaseType *, TraitReference *>, bool>
type_bound_cache;
};

} // namespace Resolver
Expand Down
171 changes: 168 additions & 3 deletions gcc/rust/typecheck/rust-tyty-bounds.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.

#include "rust-diagnostics.h"
#include "rust-hir-full-decls.h"
#include "rust-hir-map.h"
#include "rust-hir-type-bounds.h"
#include "rust-hir-trait-resolve.h"
#include "rust-substitution-mapper.h"
#include "rust-type-util.h"
#include "rust-tyty.h"

namespace Rust {
namespace Resolver {
Expand Down Expand Up @@ -92,15 +95,26 @@ TypeBoundsProbe::scan ()
TraitReference *trait_ref = TraitResolver::Resolve (*trait_path);

if (!trait_ref->is_error ())
trait_references.push_back ({trait_ref, path.second});
{
rust_debug ("Added trait: %s to type %s",
trait_ref->get_name ().c_str (),
receiver->get_name ().c_str ());
trait_references.push_back ({trait_ref, path.second});
}
}

// marker traits...
assemble_sized_builtin ();

// add auto trait bounds
// auto traits
for (auto *auto_trait : mappings.get_auto_traits ())
add_trait_bound (auto_trait);
if (should_impl_auto_trait (auto_trait))
{
rust_debug ("Added auto trait: %s to type %s",
auto_trait->get_name ().as_string ().c_str (),
receiver->get_name ().c_str ());
add_trait_bound (auto_trait);
}
}

void
Expand Down Expand Up @@ -154,6 +168,140 @@ TypeBoundsProbe::assemble_sized_builtin ()
}
}

// Recursively check if the auto trait bound is satisfied for the type
bool
TypeBoundsProbe::is_auto_trait_bound_satisfied (const TyTy::BaseType *raw,
HIR::Trait *trait)
{
// https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits
rust_debug ("checking auto trait: %s to type %s",
trait->get_name ().as_string ().c_str (),
raw->get_name ().c_str ());

// check if the trait is negative impl-ed for the type
DefId trait_defid = trait->get_mappings ().get_defid ();
for (auto bound : raw->get_specified_bounds ())
{
rust_debug ("bound: %s", bound.get_name ().c_str ());
DefId other_trait_defid = bound.get_id ();
if (other_trait_defid != UNKNOWN_DEFID && other_trait_defid == trait_defid
&& bound.get_polarity () == BoundPolarity::NegativeBound)
{
rust_debug ("negative trait %s for %s", bound.get_name ().c_str (),
raw->get_name ().c_str ());
return false;
}
}

switch (raw->get_kind ())
{
case TyTy::BOOL:
case TyTy::CHAR:
case TyTy::INT:
case TyTy::UINT:
case TyTy::FLOAT:
case TyTy::USIZE:
case TyTy::ISIZE:
return true;

case TyTy::ARRAY: {
const TyTy::BaseType *elem
= raw->as<const TyTy::ArrayType> ()->get_element_type ();
return is_auto_trait_bound_satisfied (elem, trait);
}
case TyTy::REF: {
const TyTy::BaseType *base
= raw->as<const TyTy::ReferenceType> ()->get_base ();
return is_auto_trait_bound_satisfied (base, trait);
}
break;
case TyTy::POINTER: {
const TyTy::BaseType *base
= raw->as<const TyTy::PointerType> ()->get_base ();
return is_auto_trait_bound_satisfied (base, trait);
}
break;

case TyTy::PARAM: {
const TyTy::BaseType *param_type = raw->as<const TyTy::ParamType> ();
for (const auto &bound : param_type->get_specified_bounds ())
{
DefId trait_defid = trait->get_mappings ().get_defid ();
if (trait_defid != UNKNOWN_DEFID && bound.get_id () == trait_defid)
return true;
}
return false;
}
break;
case TyTy::FNDEF:
case TyTy::FNPTR:
case TyTy::CLOSURE:
return true;

case TyTy::NEVER:
case TyTy::INFER:
case TyTy::PLACEHOLDER: {
// These types must be resolved to some concrete types.
rust_unreachable ();
}
break;

case TyTy::PROJECTION: {
const TyTy::BaseType *base
= raw->as<const TyTy::ProjectionType> ()->get ();
return is_auto_trait_bound_satisfied (base, trait);
}
break;
case TyTy::SLICE: {
const TyTy::BaseType *elem
= raw->as<const TyTy::SliceType> ()->get_element_type ();
return is_auto_trait_bound_satisfied (elem, trait);
}
break;
case TyTy::STR:
return true;

case TyTy::ADT: {
const TyTy::ADTType *adt = raw->as<const TyTy::ADTType> ();
for (const auto &variant : adt->get_variants ())
{
for (const auto &field : variant->get_fields ())
{
const TyTy::BaseType *field_type = field->get_field_type ();

if (!is_auto_trait_bound_satisfied (field_type, trait))
return false;
}
}
return true;
}
break;
case TyTy::TUPLE: {
const TyTy::TupleType *tuple = raw->as<const TyTy::TupleType> ();
for (const auto &field : tuple->get_fields ())
{
const TyTy::BaseType *field_type = field.get_tyty ();
if (!is_auto_trait_bound_satisfied (field_type, trait))
return false;
}
return true;
}
break;
case TyTy::DYNAMIC:
return false;
case TyTy::ERROR:
return false;
}
return true;
}

bool
TypeBoundsProbe::should_impl_auto_trait (HIR::Trait *trait)
{
const TyTy::BaseType *raw = receiver->destructure ();
return is_auto_trait_bound_satisfied (raw, trait);
}

void
TypeBoundsProbe::add_trait_bound (HIR::Trait *trait)
{
Expand Down Expand Up @@ -186,6 +334,23 @@ TypeBoundsProbe::assemble_builtin_candidate (LangItem::Kind lang_item)
raw->get_name ().c_str ());
}

bool
TypeBoundsProbe::bound_cache_lookup (TyTy::BaseType *tyty,
TraitReference *trait)
{
auto it = type_bound_cache.find ({tyty, trait});
if (it != type_bound_cache.end ())
return it->second;
return false;
}

void
TypeBoundsProbe::bound_cache_insert (TyTy::BaseType *tyty,
TraitReference *trait, bool satisfied)
{
type_bound_cache.insert ({{tyty, trait}, satisfied});
}

TraitReference *
TypeCheckBase::resolve_trait_path (HIR::TypePath &path)
{
Expand Down