Support Autoderef coercions

This is an incremental approach to get our coercion sites more acurate to
rustc. This allows us to support coercions which require a deref by sharing
the code from the autoderef cycle. The eventual goal here will allow us to
remove the rust-tyty-coercion rules which is messy and cannot handle logic
such as DST's very well.

Fixes #1198
This commit is contained in:
Philip Herron 2022-08-03 13:14:54 +01:00
parent 79d0f29464
commit 5d17a8b6b2
20 changed files with 330 additions and 47 deletions

View File

@ -118,6 +118,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-coercion.o \
rust/rust-hir-type-check-base.o \
rust/rust-autoderef.o \
rust/rust-substitution-mapper.o \

View File

@ -40,9 +40,12 @@ protected:
protected:
Context *get_context () { return ctx; }
tree coercion_site (tree rvalue, const TyTy::BaseType *actual,
tree coercion_site (HirId id, tree rvalue, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, Location lvalue_locus,
Location rvalue_locus);
tree coercion_site1 (tree rvalue, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, Location lvalue_locus,
Location rvalue_locus);
tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
const TyTy::BaseType *expected,

View File

@ -853,8 +853,9 @@ CompileExpr::visit (HIR::CallExpr &expr)
Location lvalue_locus
= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
Location rvalue_locus = argument->get_locus ();
rvalue = coercion_site (rvalue, actual, expected, lvalue_locus,
rvalue_locus);
rvalue
= coercion_site (argument->get_mappings ().get_hirid (), rvalue,
actual, expected, lvalue_locus, rvalue_locus);
// add it to the list
arguments.push_back (rvalue);
@ -951,8 +952,8 @@ CompileExpr::visit (HIR::CallExpr &expr)
Location lvalue_locus
= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
Location rvalue_locus = argument->get_locus ();
rvalue
= coercion_site (rvalue, actual, expected, lvalue_locus, rvalue_locus);
rvalue = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
actual, expected, lvalue_locus, rvalue_locus);
// add it to the list
args.push_back (rvalue);
@ -1069,8 +1070,8 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
Location lvalue_locus
= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
Location rvalue_locus = argument->get_locus ();
rvalue
= coercion_site (rvalue, actual, expected, lvalue_locus, rvalue_locus);
rvalue = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
actual, expected, lvalue_locus, rvalue_locus);
// add it to the list
args.push_back (rvalue);

View File

@ -177,9 +177,9 @@ public:
expr.get_rhs ()->get_mappings ().get_hirid (), &actual);
rust_assert (ok);
rvalue
= coercion_site (rvalue, actual, expected, expr.get_lhs ()->get_locus (),
expr.get_rhs ()->get_locus ());
rvalue = coercion_site (expr.get_mappings ().get_hirid (), rvalue, actual,
expected, expr.get_lhs ()->get_locus (),
expr.get_rhs ()->get_locus ());
tree assignment
= ctx->get_backend ()->assignment_statement (lvalue, rvalue,
@ -435,8 +435,9 @@ public:
if (ok)
{
rvalue = coercion_site (rvalue, actual, expected, lvalue_locus,
rvalue_locus);
rvalue
= coercion_site (argument->get_mappings ().get_hirid (), rvalue,
actual, expected, lvalue_locus, rvalue_locus);
}
// add it to the list
@ -469,7 +470,8 @@ public:
// compile/torture/struct_base_init_1.rs
if (ok)
{
rvalue = coercion_site (rvalue, actual, expected, lvalue_locus,
rvalue = coercion_site (argument->get_mappings ().get_hirid (),
rvalue, actual, expected, lvalue_locus,
rvalue_locus);
}

View File

@ -87,7 +87,8 @@ public:
Location lvalue_locus = stmt.get_pattern ()->get_locus ();
Location rvalue_locus = stmt.get_init_expr ()->get_locus ();
TyTy::BaseType *expected = ty;
init = coercion_site (init, actual, expected, lvalue_locus, rvalue_locus);
init = coercion_site (stmt.get_mappings ().get_hirid (), init, actual,
expected, lvalue_locus, rvalue_locus);
auto fnctx = ctx->peek_fn ();
if (ty->is_unit ())

View File

@ -198,9 +198,25 @@ CompileStructExprField::visit (HIR::StructExprFieldIdentifier &field)
// Shared methods in compilation
tree
HIRCompileBase::coercion_site (tree rvalue, const TyTy::BaseType *rval,
HIRCompileBase::coercion_site (HirId id, tree rvalue,
const TyTy::BaseType *rval,
const TyTy::BaseType *lval,
Location lvalue_locus, Location rvalue_locus)
{
std::vector<Resolver::Adjustment> *adjustments = nullptr;
bool ok = ctx->get_tyctx ()->lookup_autoderef_mappings (id, &adjustments);
if (ok)
{
rvalue = resolve_adjustements (*adjustments, rvalue, rvalue_locus);
}
return coercion_site1 (rvalue, rval, lval, lvalue_locus, rvalue_locus);
}
tree
HIRCompileBase::coercion_site1 (tree rvalue, const TyTy::BaseType *rval,
const TyTy::BaseType *lval,
Location lvalue_locus, Location rvalue_locus)
{
if (rvalue == error_mark_node)
return error_mark_node;
@ -227,8 +243,8 @@ HIRCompileBase::coercion_site (tree rvalue, const TyTy::BaseType *rval,
tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
tree coerced
= coercion_site (deref_rvalue, act->get_base (), exp->get_base (),
lvalue_locus, rvalue_locus);
= coercion_site1 (deref_rvalue, act->get_base (), exp->get_base (),
lvalue_locus, rvalue_locus);
if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
return coerced;
@ -269,8 +285,10 @@ HIRCompileBase::coercion_site (tree rvalue, const TyTy::BaseType *rval,
rust_assert (actual_base != nullptr);
tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
tree coerced = coercion_site (deref_rvalue, actual_base, exp->get_base (),
lvalue_locus, rvalue_locus);
tree coerced
= coercion_site1 (deref_rvalue, actual_base, exp->get_base (),
lvalue_locus, rvalue_locus);
if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
return coerced;

View File

@ -157,7 +157,7 @@ protected:
// type
virtual void try_hook (const TyTy::BaseType &);
bool cycle (const TyTy::BaseType *receiver);
virtual bool cycle (const TyTy::BaseType *receiver);
bool try_autoderefed (const TyTy::BaseType *r);

View File

@ -0,0 +1,92 @@
// 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
// <http://www.gnu.org/licenses/>.
#include "rust-coercion.h"
namespace Rust {
namespace Resolver {
AutoderefTypeCoercion::CoercionResult
AutoderefTypeCoercion::Coerce (const TyTy::BaseType *receiver,
const TyTy::BaseType *expected, Location locus)
{
AutoderefTypeCoercion resolver (expected, locus);
bool ok = resolver.cycle (receiver);
return ok ? resolver.try_result : CoercionResult::get_error ();
}
AutoderefTypeCoercion::AutoderefTypeCoercion (const TyTy::BaseType *expected,
Location locus)
: AutoderefCycle (false), mappings (Analysis::Mappings::get ()),
context (TypeCheckContext::get ()), expected (expected), locus (locus),
try_result (CoercionResult::get_error ())
{}
bool
AutoderefTypeCoercion::cycle (const TyTy::BaseType *receiver)
{
// FIXME this is not finished and might be super simplified
// see:
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
if (receiver->get_kind () == TyTy::TypeKind::REF
&& expected->get_kind () == TyTy::TypeKind::REF)
{
// if we expect to get a mutable pointer we can't get that from an
// immutable one so we have to be careful
const auto &receiver_ref
= static_cast<const TyTy::ReferenceType &> (*receiver);
const auto &expected_ref
= static_cast<const TyTy::ReferenceType &> (*expected);
// we can allow for mutability changes here by casting down from
// mutability eg: mut vs const, we cant take a mutable reference from a
// const eg: const vs mut we can take a const reference from a mutable
// one
bool mutability_ok
= !expected_ref.is_mutable ()
|| (expected_ref.is_mutable () == receiver_ref.is_mutable ());
if (!mutability_ok)
{
RichLocation r (locus);
r.add_range (mappings->lookup_location (receiver_ref.get_ref ()));
r.add_range (mappings->lookup_location (expected_ref.get_ref ()));
rust_error_at (r, "mismatched mutability");
return false;
}
}
return AutoderefCycle::cycle (receiver);
}
bool
AutoderefTypeCoercion::select (const TyTy::BaseType &autoderefed)
{
if (autoderefed.can_eq (expected, false))
{
try_result = CoercionResult{adjustments, autoderefed.clone ()};
return true;
}
return false;
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,70 @@
// 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
// <http://www.gnu.org/licenses/>.
#ifndef RUST_COERCION
#define RUST_COERCION
#include "rust-autoderef.h"
#include "rust-hir-type-check.h"
namespace Rust {
namespace Resolver {
class AutoderefTypeCoercion : protected AutoderefCycle
{
public:
struct CoercionResult
{
std::vector<Adjustment> adjustments;
TyTy::BaseType *tyty;
bool is_error ()
{
return tyty == nullptr || tyty->get_kind () == TyTy::TypeKind::ERROR;
}
static CoercionResult get_error () { return CoercionResult{{}, nullptr}; }
};
static CoercionResult Coerce (const TyTy::BaseType *receiver,
const TyTy::BaseType *expected, Location locus);
protected:
AutoderefTypeCoercion (const TyTy::BaseType *expected, Location locus);
bool cycle (const TyTy::BaseType *receiver) override;
bool select (const TyTy::BaseType &autoderefed) override;
private:
// context info
Analysis::Mappings *mappings;
TypeCheckContext *context;
// search
const TyTy::BaseType *expected;
Location locus;
// mutable fields
CoercionResult try_result;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_COERCION

View File

@ -17,6 +17,7 @@
// <http://www.gnu.org/licenses/>.
#include "rust-hir-type-check-base.h"
#include "rust-coercion.h"
namespace Rust {
namespace Resolver {
@ -328,10 +329,24 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, Location locus)
}
TyTy::BaseType *
TypeCheckBase::coercion_site (TyTy::BaseType *lhs, TyTy::BaseType *rhs,
Location)
TypeCheckBase::coercion_site (HirId id, TyTy::BaseType *expected,
TyTy::BaseType *expr, Location locus)
{
return lhs->coerce (rhs);
auto context = TypeCheckContext::get ();
if (expected->get_kind () == TyTy::TypeKind::ERROR
|| expr->get_kind () == TyTy::TypeKind::ERROR)
return expr;
// can we autoderef it?
auto result = AutoderefTypeCoercion::Coerce (expr, expected, locus);
if (!result.is_error ())
{
// save any adjustments
context->insert_autoderef_mappings (id, std::move (result.adjustments));
return expected->coerce (result.tyty);
}
return expected->coerce (expr);
}
} // namespace Resolver

View File

@ -39,7 +39,7 @@ public:
virtual ~TypeCheckBase () {}
static TyTy::BaseType *coercion_site (TyTy::BaseType *lhs,
static TyTy::BaseType *coercion_site (HirId id, TyTy::BaseType *lhs,
TyTy::BaseType *rhs, Location locus);
protected:

View File

@ -230,7 +230,8 @@ public:
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
coercion_site (lhs, rhs, expr.get_locus ());
coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs,
expr.get_locus ());
}
void visit (HIR::CompoundAssignmentExpr &expr) override

View File

@ -99,7 +99,8 @@ public:
if (specified_ty != nullptr && init_expr_ty != nullptr)
{
// FIXME use this result and look at the regressions
coercion_site (specified_ty, init_expr_ty, stmt.get_locus ());
coercion_site (stmt.get_mappings ().get_hirid (), specified_ty,
init_expr_ty, stmt.get_locus ());
context->insert_type (stmt_pattern.get_pattern_mappings (),
specified_ty);
}

View File

@ -222,7 +222,8 @@ TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field)
TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
resolved_field_value_expr
= coercion_site (field_type->get_field_type (), value, field.get_locus ());
= coercion_site (field.get_mappings ().get_hirid (),
field_type->get_field_type (), value, field.get_locus ());
if (resolved_field_value_expr != nullptr)
{
fields_assigned.insert (field.field_name);
@ -252,7 +253,8 @@ TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field)
TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
resolved_field_value_expr
= coercion_site (field_type->get_field_type (), value, field.get_locus ());
= coercion_site (field.get_mappings ().get_hirid (),
field_type->get_field_type (), value, field.get_locus ());
if (resolved_field_value_expr != nullptr)
{
fields_assigned.insert (field_name);
@ -287,7 +289,8 @@ TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr);
resolved_field_value_expr
= coercion_site (field_type->get_field_type (), value, field.get_locus ());
= coercion_site (field.get_mappings ().get_hirid (),
field_type->get_field_type (), value, field.get_locus ());
if (resolved_field_value_expr != nullptr)
{

View File

@ -58,7 +58,9 @@ TypeCheckCallExpr::visit (ADTType &type)
return;
}
auto res = field_tyty->coerce (arg);
auto res = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), field_tyty, arg,
argument->get_locus ());
if (res->get_kind () == TyTy::TypeKind::ERROR)
{
return;
@ -123,8 +125,9 @@ TypeCheckCallExpr::visit (FnType &type)
if (i < type.num_params ())
{
auto fnparam = type.param_at (i);
auto resolved_argument_type
= fnparam.second->coerce (argument_expr_tyty);
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam.second,
argument_expr_tyty, argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
@ -176,9 +179,9 @@ TypeCheckCallExpr::visit (FnPtr &type)
return;
}
auto resolved_argument_type
= Resolver::TypeCheckBase::coercion_site (fnparam, argument_expr_tyty,
argument->get_locus ());
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam, argument_expr_tyty,
argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
@ -234,10 +237,9 @@ TypeCheckMethodCallExpr::visit (FnType &type)
return;
}
auto resolved_argument_type
= Resolver::TypeCheckBase::coercion_site (fnparam.second,
argument_expr_tyty,
argument->get_locus ());
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam.second,
argument_expr_tyty, argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),

View File

@ -3,10 +3,9 @@ struct GenericStruct<T>(T, usize);
fn main() {
let a2;
a2 = GenericStruct::<i8, i32>(1, 456); // { dg-error "generic item takes at most 1 type arguments but 2 were supplied" }
// { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
// { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-2 }
// { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-3 }
// { dg-error {expected \[T\?\] got \[<tyty::error>\]} "" { target *-*-* } .-4 }
// { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
// { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-2 }
// { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-3 }
let b2: i32 = a2.0;
// { dg-error {Expected Tuple or ADT got: T\?} "" { target *-*-* } .-1 }

View File

@ -1,5 +1,6 @@
fn main() {
let a = &123;
let b: &mut i32 = a;
// { dg-error "expected .&mut i32. got .& i32." "" { target *-*-* } .-1 }
// { dg-error "mismatched mutability" "" { target *-*-* } .-1 }
// { dg-error "expected .&mut i32. got .& i32." "" { target *-*-* } .-2 }
}

View File

@ -3,6 +3,5 @@ fn main() {
let mut x;
x = true;
x = x + 2; // { dg-error "cannot apply this operator to types bool and <integer>" }
// { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
// { dg-error {expected \[bool\] got \[<tyty::error>\]} "" { target *-*-* } .-2 }
// { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
}

View File

@ -7,5 +7,4 @@ fn main() {
// { dg-error {failed to type resolve expression} "" { target *-*-* } .-2 }
// { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-3 }
// { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-4 }
// { dg-error {expected \[T\?\] got \[<tyty::error>\]} "" { target *-*-* } .-5 }
}

View File

@ -0,0 +1,75 @@
/* { dg-output "foo_deref\nimm_deref\n123\n" } */
extern "C" {
fn printf(s: *const i8, ...);
}
#[lang = "deref"]
pub trait Deref {
type Target;
fn deref(&self) -> &Self::Target;
}
impl<T> Deref for &T {
type Target = T;
fn deref(&self) -> &T {
unsafe {
let a = "imm_deref\n\0";
let b = a as *const str;
let c = b as *const i8;
printf(c);
}
*self
}
}
impl<T> Deref for &mut T {
type Target = T;
fn deref(&self) -> &T {
unsafe {
let a = "mut_deref\n\0";
let b = a as *const str;
let c = b as *const i8;
printf(c);
}
*self
}
}
struct Foo<T>(T);
impl<T> Deref for Foo<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe {
let a = "foo_deref\n\0";
let b = a as *const str;
let c = b as *const i8;
printf(c);
}
&self.0
}
}
fn main() -> i32 {
let foo: Foo<i32> = Foo(123);
let bar: &i32 = &foo;
unsafe {
let a = "%i\n\0";
let b = a as *const str;
let c = b as *const i8;
printf(c, *bar);
}
0
}