Skip to content

Commit

Permalink
Support Slices from rustc libcore 1.49.0
Browse files Browse the repository at this point in the history
This is unfortunatly a mega commit, in testing gccrs against the slice code
which is highly generic stress tested our implementation of generics and
poked the hole in or lack of support of generic higher ranked trait bounds
and more specificily generic associated types. More refactoring is needed
to eventually remove the setup_associated_types and replace it entirely
with this new setup_associated_types2 which takes into account the trait
bound receiver and its predicate.

In order to support slices, the code in libcore defines an index lang item

```rust
impl<T, I> Index<I> for [T]
where
    I: SliceIndex<[T]>,
{
    type Output = I::Output;

    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}
```

This is the entry point where by the self here is a generic slice. So in
our case we have:

```rust
let a = [1, 2, 3, 4, 5];
let b = &a[1..3];
```

'a' is an array and b is our desired slice, so we must remember that from
algebraic data type constructor. But our receiver is still an array, so in
order to be able to call this index lang item we must 'unsize' our array
(see #1045) this allows for method resolution to adjust an array into a
FatPtr which is simply a struct containing reference to the array and the
capacity (GCC MAX_DOMAIN) of the underlying array data type. So now we are
able to infer the substituions for this index fn call to:

```
fn index(&self : [<integer>], index: Range<integer>)
  -> &I::Output->placeholder
```

The complex piece here is the Higher ranked trait bound:

```
where I: SliceIndex<[T]>
```

So in this method call no generic arguments are specified so we must try
and infer the types. So during monomorphization the inference variables
need to be recursively propogated into the higher ranked trait bound. So
that the higher ranked trait bound looks like:

```
SliceIndex<[<integer>]> // like we seen earlier for the Self type
```

The monomorphization stage also needs to take into account the higher
ranked trait bound's type which is 'I' and infered to be: Range<integer>.
This is where specialization needs to occur.

```rust
unsafe impl<T> SliceIndex<[T]> for Range<usize> {
    type Output = [T];

    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
        unsafe {
            let a: *const T = slice.as_ptr();
            let b: *const T = a.add(self.start);
            slice_from_raw_parts(b, self.end - self.start)
        }
    }

    fn index(self, slice: &[T]) -> &[T] {
        unsafe { &*self.get_unchecked(slice) }
    }
}
```

So now we need to compute the constrained type-parameters for this
specialized impl block. And in this case is fairly simple:

```
  impl<T> SliceIndex<[T]> for Range<usize>
  vs
  I: SliceIndex<[<integer>]> and Range<<integer>>
```

Here we need to compute that T is <integer>, which is required since
associated type Output is used in our original method call and this
is generic which requires us to set it up but both the Self type or
the trait bound here in this impl block could be generic so special
care needs to be taken to compute this safely. Once the constrained
types are computer we can also unify the Self types which specializes
our original Range<integer> type into the correct Range<usize> that
this trait bound expects. We used a callback here when we reusively
pass down the SubstitutionArgumentMappings when any Parameter type
is substitued we get a callback to hold a set of mappings in a generic
way what generic types are being substituted.

From all of this work this stressed our generics implementation to
breaking point due to the use of the generic trait bound which was
not supported and it also exposed many bugs in our implementation.
This is why I feel it is best to keep this a large patch as so much
of this patch will cause regressions if we don't keep it together.

One of the main changes we have made is how we handle parameters
substitution for example we might have a generic such as '&Y' but
this gets substituted with Y=T which is a new type parameter. Before
we used to directly just change this from &Y to &T which is correct
but this looses context from the generic argument bindings. So now
we maintain the information that &Y changes to &(Y=T) so that we see
Y was substutued with T so that subsequent substitutions or inferences
can change Y=?T and correctly map &Y to &(Y=T) to &(Y=?T).

The other major piece which was changed during this patch was how
we perform the method resolution on higher ranked trait bound calls
where we compute the specified bound possible candidates once so that
in the case:

```
trait Bar {
  fn baz(&self)
}

fn <T:Bar> foo(a: &T) {
  a.baz()
}
```

Here the type parameter T gets derefed to find the specified bound of
Bar which contains the method baz. This means that we try calling baz
with T vs &T which fails then we try the reference type T again. This
results into two useless adjustments of indirection and referencing but
GCC optimizes this away. Before this patch we computed the specified bound
for each attempt which was wrong.

Fixes #849
  • Loading branch information
philberty committed Apr 11, 2022
1 parent 69d6fdd commit 0e686c0
Show file tree
Hide file tree
Showing 21 changed files with 804 additions and 289 deletions.
19 changes: 15 additions & 4 deletions gcc/rust/backend/rust-compile-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -747,8 +747,9 @@ CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,

auto root = receiver->get_root ();
std::vector<Resolver::PathProbeCandidate> candidates
= Resolver::PathProbeType::Probe (root, segment, true, false, true);

= Resolver::PathProbeType::Probe (root, segment, true /* probe_impls */,
false /* probe_bounds */,
true /* ignore_mandatory_trait_items */);
if (candidates.size () == 0)
{
// this means we are defaulting back to the trait_item if
Expand Down Expand Up @@ -776,12 +777,22 @@ CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,
rust_assert (candidates.size () == 1);
auto &candidate = candidates.at (0);
rust_assert (candidate.is_impl_candidate ());
rust_assert (candidate.ty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *candidate_call = static_cast<TyTy::FnType *> (candidate.ty);

HIR::ImplItem *impl_item = candidate.item.impl.impl_item;
if (!fntype->has_subsititions_defined ())
if (!candidate_call->has_subsititions_defined ())
return CompileInherentImplItem::Compile (impl_item, ctx);

return CompileInherentImplItem::Compile (impl_item, ctx, fntype);
TyTy::BaseType *monomorphized = candidate_call;
if (candidate_call->needs_generic_substitutions ())
{
TyTy::BaseType *infer_impl_call
= candidate_call->infer_substitions (expr_locus);
monomorphized = infer_impl_call->unify (fntype);
}

return CompileInherentImplItem::Compile (impl_item, ctx, monomorphized);
}
}

Expand Down
18 changes: 1 addition & 17 deletions gcc/rust/backend/rust-compile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ CompileCrate::~CompileCrate () {}

void
CompileCrate::Compile (HIR::Crate &crate, Context *ctx)

{
CompileCrate c (crate, ctx);
c.go ();
Expand Down Expand Up @@ -383,26 +382,11 @@ HIRCompileBase::compute_address_for_trait_item (
= self_bound->lookup_associated_item (ref->get_identifier ());
rust_assert (!associated_self_item.is_error ());

// apply any generic arguments from this predicate
TyTy::BaseType *mono1 = associated_self_item.get_tyty_for_receiver (self);
TyTy::BaseType *mono2 = nullptr;
if (predicate->has_generic_args ())
{
mono2 = associated_self_item.get_tyty_for_receiver (
self, predicate->get_generic_args ());
}
else
{
mono2 = associated_self_item.get_tyty_for_receiver (self);
}
rust_assert (mono1 != nullptr);
rust_assert (mono1->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *assocated_item_ty1 = static_cast<TyTy::FnType *> (mono1);

rust_assert (mono2 != nullptr);
rust_assert (mono2->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *assocated_item_ty2 = static_cast<TyTy::FnType *> (mono2);

// Lookup the impl-block for the associated impl_item if it exists
HIR::Function *associated_function = nullptr;
for (auto &impl_item : associated_impl_block->get_impl_items ())
Expand Down Expand Up @@ -434,7 +418,7 @@ HIRCompileBase::compute_address_for_trait_item (
{
TyTy::SubstitutionArgumentMappings mappings
= assocated_item_ty1->solve_missing_mappings_from_this (
*assocated_item_ty2, *lookup_fntype);
*trait_item_fntype, *lookup_fntype);
lookup_fntype = lookup_fntype->handle_substitions (mappings);
}

Expand Down
64 changes: 33 additions & 31 deletions gcc/rust/typecheck/rust-hir-dot-operator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,11 @@ MethodResolver::Try (const TyTy::BaseType *r,
PathProbeCandidate c = PathProbeCandidate::get_error ();
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds
= r->get_specified_bounds ();
const std::vector<MethodResolver::predicate_candidate> predicate_items
= get_predicate_items (segment_name, *r, specified_bounds);

// 1. try raw
MethodResolver raw (*r, segment_name, specified_bounds);
MethodResolver raw (*r, segment_name, predicate_items);
c = raw.select ();
if (!c.is_error ())
{
Expand All @@ -139,7 +141,7 @@ MethodResolver::Try (const TyTy::BaseType *r,
TyTy::ReferenceType *r1
= new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
Mutability::Imm);
MethodResolver imm_ref (*r1, segment_name, specified_bounds);
MethodResolver imm_ref (*r1, segment_name, predicate_items);
c = imm_ref.select ();
if (!c.is_error ())
{
Expand All @@ -152,7 +154,7 @@ MethodResolver::Try (const TyTy::BaseType *r,
TyTy::ReferenceType *r2
= new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
Mutability::Mut);
MethodResolver mut_ref (*r2, segment_name, specified_bounds);
MethodResolver mut_ref (*r2, segment_name, predicate_items);
c = mut_ref.select ();
if (!c.is_error ())
{
Expand Down Expand Up @@ -288,27 +290,6 @@ MethodResolver::select ()
TyTy::FnType *fntype;
};

std::vector<precdicate_candidate> predicate_items;
for (auto &bound : specified_bounds)
{
TyTy::TypeBoundPredicateItem lookup
= bound.lookup_associated_item (segment_name.as_string ());
if (lookup.is_error ())
continue;

bool is_fn = lookup.get_raw_item ()->get_trait_item_type ()
== TraitItemReference::TraitItemType::FN;
if (!is_fn)
continue;

TyTy::BaseType *ty = lookup.get_raw_item ()->get_tyty ();
rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);

precdicate_candidate candidate{lookup, fnty};
predicate_items.push_back (candidate);
}

for (auto impl_item : inherent_impl_fns)
{
TyTy::FnType *fn = impl_item.ty;
Expand Down Expand Up @@ -342,9 +323,9 @@ MethodResolver::select ()
}
}

for (auto predicate : predicate_items)
for (const auto &predicate : predicate_items)
{
TyTy::FnType *fn = predicate.fntype;
const TyTy::FnType *fn = predicate.fntype;
rust_assert (fn->is_method ());

TyTy::BaseType *fn_self = fn->get_self_type ();
Expand All @@ -355,20 +336,41 @@ MethodResolver::select ()
const TraitItemReference *trait_item
= predicate.lookup.get_raw_item ();

TyTy::BaseType *subst = predicate.lookup.get_tyty_for_receiver (
receiver.get_root (),
predicate.lookup.get_parent ()->get_generic_args ());

PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
nullptr};
return PathProbeCandidate (
PathProbeCandidate::CandidateType::TRAIT_FUNC, subst,
PathProbeCandidate::CandidateType::TRAIT_FUNC, fn->clone (),
trait_item->get_locus (), c);
}
}

return PathProbeCandidate::get_error ();
}

std::vector<MethodResolver::predicate_candidate>
MethodResolver::get_predicate_items (
const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
{
std::vector<predicate_candidate> predicate_items;
for (auto &bound : specified_bounds)
{
TyTy::TypeBoundPredicateItem lookup
= bound.lookup_associated_item (segment_name.as_string ());
if (lookup.is_error ())
continue;

TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
if (ty->get_kind () == TyTy::TypeKind::FNDEF)
{
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
predicate_candidate candidate{lookup, fnty};
predicate_items.push_back (candidate);
}
}

return predicate_items;
}

} // namespace Resolver
} // namespace Rust
20 changes: 15 additions & 5 deletions gcc/rust/typecheck/rust-hir-dot-operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,32 @@ class MethodResolver : public TypeCheckBase
bool autoderef_flag = false);

protected:
struct predicate_candidate
{
TyTy::TypeBoundPredicateItem lookup;
TyTy::FnType *fntype;
};

static MethodCandidate Try (const TyTy::BaseType *r,
const HIR::PathIdentSegment &segment_name,
std::vector<Adjustment> &adjustments);

static std::vector<predicate_candidate> get_predicate_items (
const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);

PathProbeCandidate select ();

MethodResolver (const TyTy::BaseType &receiver,
const HIR::PathIdentSegment &segment_name,
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
MethodResolver (
const TyTy::BaseType &receiver, const HIR::PathIdentSegment &segment_name,
const std::vector<MethodResolver::predicate_candidate> &predicate_items)
: receiver (receiver), segment_name (segment_name),
specified_bounds (specified_bounds)
predicate_items (predicate_items)
{}

const TyTy::BaseType &receiver;
const HIR::PathIdentSegment &segment_name;
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds;
const std::vector<MethodResolver::predicate_candidate> &predicate_items;
};

} // namespace Resolver
Expand Down
5 changes: 5 additions & 0 deletions gcc/rust/typecheck/rust-hir-trait-ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ class TraitReference
return hir_trait_ref->get_mappings ();
}

DefId get_defid () const { return get_mappings ().get_defid (); }

bool lookup_hir_trait_item (const HIR::TraitItem &item,
TraitItemReference **ref)
{
Expand Down Expand Up @@ -436,6 +438,9 @@ class AssociatedImplTrait

void setup_associated_types ();

void setup_associated_types2 (const TyTy::BaseType *self,
const TyTy::TypeBoundPredicate &bound);

void reset_associated_types ();

TyTy::BaseType *get_projected_type (const TraitItemReference *trait_item_ref,
Expand Down
Loading

0 comments on commit 0e686c0

Please sign in to comment.