Skip to content

Commit bc5f02f

Browse files
committed
gccrs: Add initial support for deffered operator overload resolution
In the test case: fn test (len: usize) -> u64 { let mut i = 0; let mut out = 0; if i + 3 < len { out = 123; } out } The issue is to determine the correct type of 'i', out is simple because it hits a coercion site in the resturn position for u64. But 'i + 3', 'i' is an integer infer variable and the same for the literal '3'. So when it comes to resolving the type for the Add expression we hit the resolve the operator overload code and because of this: macro_rules! add_impl { ($($t:ty)*) => ($( impl Add for $t { type Output = $t; #[inline] #[rustc_inherit_overflow_checks] fn add(self, other: $t) -> $t { self + other } } )*) } add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } This means the resolution for 'i + 3' is ambigious because it could be any of these Add implementations. But because we unify against the '< len' where len is defined as usize later in the resolution we determine 'i' is actually a usize. Which means if we defer the resolution of this operator overload in the ambigious case we can simply resolve it at the end. Fixes #3916 gcc/rust/ChangeLog: * hir/tree/rust-hir-expr.cc (OperatorExprMeta::OperatorExprMeta): track the rhs * hir/tree/rust-hir-expr.h: likewise * hir/tree/rust-hir-path.h: get rid of old comments * typecheck/rust-hir-trait-reference.cc (TraitReference::get_trait_substs): return references instead of copy * typecheck/rust-hir-trait-reference.h: update header * typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::ResolveOpOverload): write ambigious operator overloads to a table and try to resolve it at the end * typecheck/rust-hir-type-check-expr.h: new static helper * typecheck/rust-hir-type-check.h (struct DeferredOpOverload): new model to defer resolution * typecheck/rust-typecheck-context.cc (TypeCheckContext::lookup_operator_overload): new (TypeCheckContext::compute_ambigious_op_overload): likewise (TypeCheckContext::compute_inference_variables): likewise gcc/testsuite/ChangeLog: * rust/compile/issue-3916.rs: New test. Signed-off-by: Philip Herron <[email protected]>
1 parent 07657bd commit bc5f02f

10 files changed

+267
-19
lines changed

gcc/rust/hir/tree/rust-hir-expr.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// <http://www.gnu.org/licenses/>.
1818

1919
#include "rust-hir-expr.h"
20+
#include "rust-hir-map.h"
2021
#include "rust-operators.h"
2122
#include "rust-hir-stmt.h"
2223

@@ -1321,37 +1322,40 @@ AsyncBlockExpr::operator= (AsyncBlockExpr const &other)
13211322
OperatorExprMeta::OperatorExprMeta (HIR::CompoundAssignmentExpr &expr)
13221323
: node_mappings (expr.get_mappings ()),
13231324
lvalue_mappings (expr.get_expr ().get_mappings ()),
1324-
locus (expr.get_locus ())
1325+
rvalue_mappings (expr.get_rhs ().get_mappings ()), locus (expr.get_locus ())
13251326
{}
13261327

13271328
OperatorExprMeta::OperatorExprMeta (HIR::ArithmeticOrLogicalExpr &expr)
13281329
: node_mappings (expr.get_mappings ()),
13291330
lvalue_mappings (expr.get_expr ().get_mappings ()),
1330-
locus (expr.get_locus ())
1331+
rvalue_mappings (expr.get_rhs ().get_mappings ()), locus (expr.get_locus ())
13311332
{}
13321333

13331334
OperatorExprMeta::OperatorExprMeta (HIR::NegationExpr &expr)
13341335
: node_mappings (expr.get_mappings ()),
13351336
lvalue_mappings (expr.get_expr ().get_mappings ()),
1337+
rvalue_mappings (Analysis::NodeMapping::get_error ()),
13361338
locus (expr.get_locus ())
13371339
{}
13381340

13391341
OperatorExprMeta::OperatorExprMeta (HIR::DereferenceExpr &expr)
13401342
: node_mappings (expr.get_mappings ()),
13411343
lvalue_mappings (expr.get_expr ().get_mappings ()),
1344+
rvalue_mappings (Analysis::NodeMapping::get_error ()),
13421345
locus (expr.get_locus ())
13431346
{}
13441347

13451348
OperatorExprMeta::OperatorExprMeta (HIR::ArrayIndexExpr &expr)
13461349
: node_mappings (expr.get_mappings ()),
13471350
lvalue_mappings (expr.get_array_expr ().get_mappings ()),
1351+
rvalue_mappings (expr.get_index_expr ().get_mappings ()),
13481352
locus (expr.get_locus ())
13491353
{}
13501354

13511355
OperatorExprMeta::OperatorExprMeta (HIR::ComparisonExpr &expr)
13521356
: node_mappings (expr.get_mappings ()),
13531357
lvalue_mappings (expr.get_expr ().get_mappings ()),
1354-
locus (expr.get_locus ())
1358+
rvalue_mappings (expr.get_rhs ().get_mappings ()), locus (expr.get_locus ())
13551359
{}
13561360

13571361
InlineAsmOperand::In::In (

gcc/rust/hir/tree/rust-hir-expr.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "rust-hir-attrs.h"
2828
#include "rust-expr.h"
2929
#include "rust-hir-map.h"
30+
#include "rust-mapping-common.h"
3031

3132
namespace Rust {
3233
namespace HIR {
@@ -2892,18 +2893,45 @@ class OperatorExprMeta
28922893

28932894
OperatorExprMeta (HIR::ComparisonExpr &expr);
28942895

2896+
OperatorExprMeta (const OperatorExprMeta &other)
2897+
: node_mappings (other.node_mappings),
2898+
lvalue_mappings (other.lvalue_mappings),
2899+
rvalue_mappings (other.rvalue_mappings), locus (other.locus)
2900+
{}
2901+
2902+
OperatorExprMeta &operator= (const OperatorExprMeta &other)
2903+
{
2904+
node_mappings = other.node_mappings;
2905+
lvalue_mappings = other.lvalue_mappings;
2906+
rvalue_mappings = other.rvalue_mappings;
2907+
locus = other.locus;
2908+
2909+
return *this;
2910+
}
2911+
28952912
const Analysis::NodeMapping &get_mappings () const { return node_mappings; }
28962913

28972914
const Analysis::NodeMapping &get_lvalue_mappings () const
28982915
{
28992916
return lvalue_mappings;
29002917
}
29012918

2919+
const Analysis::NodeMapping &get_rvalue_mappings () const
2920+
{
2921+
return rvalue_mappings;
2922+
}
2923+
2924+
bool has_rvalue_mappings () const
2925+
{
2926+
return rvalue_mappings.get_hirid () != UNKNOWN_HIRID;
2927+
}
2928+
29022929
location_t get_locus () const { return locus; }
29032930

29042931
private:
2905-
const Analysis::NodeMapping node_mappings;
2906-
const Analysis::NodeMapping lvalue_mappings;
2932+
Analysis::NodeMapping node_mappings;
2933+
Analysis::NodeMapping lvalue_mappings;
2934+
Analysis::NodeMapping rvalue_mappings;
29072935
location_t locus;
29082936
};
29092937

gcc/rust/hir/tree/rust-hir-path.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ class PathIdentSegment
4141
: segment_name (std::move (segment_name))
4242
{}
4343

44-
/* TODO: insert check in constructor for this? Or is this a semantic error
45-
* best handled then? */
44+
PathIdentSegment (const PathIdentSegment &other)
45+
: segment_name (other.segment_name)
46+
{}
4647

47-
/* TODO: does this require visitor? pretty sure this isn't polymorphic, but
48-
* not entirely sure */
48+
PathIdentSegment &operator= (PathIdentSegment const &other)
49+
{
50+
segment_name = other.segment_name;
51+
return *this;
52+
}
4953

5054
// Creates an error PathIdentSegment.
5155
static PathIdentSegment create_error () { return PathIdentSegment (""); }

gcc/rust/typecheck/rust-hir-trait-reference.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,13 @@ TraitReference::trait_has_generics () const
432432
return !trait_substs.empty ();
433433
}
434434

435-
std::vector<TyTy::SubstitutionParamMapping>
435+
std::vector<TyTy::SubstitutionParamMapping> &
436+
TraitReference::get_trait_substs ()
437+
{
438+
return trait_substs;
439+
}
440+
441+
const std::vector<TyTy::SubstitutionParamMapping> &
436442
TraitReference::get_trait_substs () const
437443
{
438444
return trait_substs;

gcc/rust/typecheck/rust-hir-trait-reference.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,9 @@ class TraitReference
224224

225225
bool trait_has_generics () const;
226226

227-
std::vector<TyTy::SubstitutionParamMapping> get_trait_substs () const;
227+
std::vector<TyTy::SubstitutionParamMapping> &get_trait_substs ();
228+
229+
const std::vector<TyTy::SubstitutionParamMapping> &get_trait_substs () const;
228230

229231
bool satisfies_bound (const TraitReference &reference) const;
230232

gcc/rust/typecheck/rust-hir-type-check-expr.cc

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// <http://www.gnu.org/licenses/>.
1818

1919
#include "optional.h"
20+
#include "rust-common.h"
2021
#include "rust-hir-expr.h"
2122
#include "rust-system.h"
2223
#include "rust-tyty-call.h"
@@ -59,6 +60,19 @@ TypeCheckExpr::Resolve (HIR::Expr &expr)
5960
return resolver.infered;
6061
}
6162

63+
TyTy::BaseType *
64+
TypeCheckExpr::ResolveOpOverload (LangItem::Kind lang_item_type,
65+
HIR::OperatorExprMeta expr,
66+
TyTy::BaseType *lhs, TyTy::BaseType *rhs,
67+
HIR::PathIdentSegment specified_segment)
68+
{
69+
TypeCheckExpr resolver;
70+
71+
resolver.resolve_operator_overload (lang_item_type, expr, lhs, rhs,
72+
specified_segment);
73+
return resolver.infered;
74+
}
75+
6276
void
6377
TypeCheckExpr::visit (HIR::TupleIndexExpr &expr)
6478
{
@@ -1885,7 +1899,16 @@ TypeCheckExpr::resolve_operator_overload (
18851899
// probe for the lang-item
18861900
if (!lang_item_defined)
18871901
return false;
1902+
18881903
DefId &respective_lang_item_id = lang_item_defined.value ();
1904+
auto def_lookup = mappings.lookup_defid (respective_lang_item_id);
1905+
rust_assert (def_lookup.has_value ());
1906+
1907+
HIR::Item *def_item = def_lookup.value ();
1908+
rust_assert (def_item->get_item_kind () == HIR::Item::ItemKind::Trait);
1909+
HIR::Trait &trait = *static_cast<HIR::Trait *> (def_item);
1910+
TraitReference *defid_trait_reference = TraitResolver::Resolve (trait);
1911+
rust_assert (!defid_trait_reference->is_error ());
18891912

18901913
// we might be in a static or const context and unknown is fine
18911914
TypeCheckContextItem current_context = TypeCheckContextItem::get_error ();
@@ -1929,15 +1952,49 @@ TypeCheckExpr::resolve_operator_overload (
19291952

19301953
if (selected_candidates.size () > 1)
19311954
{
1932-
// mutliple candidates
1933-
rich_location r (line_table, expr.get_locus ());
1934-
for (auto &c : resolved_candidates)
1935-
r.add_range (c.candidate.locus);
1955+
auto infer
1956+
= TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty ();
1957+
auto trait_subst = defid_trait_reference->get_trait_substs ();
1958+
rust_assert (trait_subst.size () > 0);
19361959

1937-
rust_error_at (
1938-
r, "multiple candidates found for possible operator overload");
1960+
TyTy::TypeBoundPredicate pred (respective_lang_item_id, trait_subst,
1961+
BoundPolarity::RegularBound,
1962+
expr.get_locus ());
19391963

1940-
return false;
1964+
std::vector<TyTy::SubstitutionArg> mappings;
1965+
auto &self_param_mapping = trait_subst[0];
1966+
mappings.push_back (TyTy::SubstitutionArg (&self_param_mapping, lhs));
1967+
1968+
if (rhs != nullptr)
1969+
{
1970+
rust_assert (trait_subst.size () == 2);
1971+
auto &rhs_param_mapping = trait_subst[1];
1972+
mappings.push_back (TyTy::SubstitutionArg (&rhs_param_mapping, lhs));
1973+
}
1974+
1975+
std::map<std::string, TyTy::BaseType *> binding_args;
1976+
binding_args["Output"] = infer;
1977+
1978+
TyTy::SubstitutionArgumentMappings arg_mappings (mappings, binding_args,
1979+
TyTy::RegionParamList (
1980+
trait_subst.size ()),
1981+
expr.get_locus ());
1982+
pred.apply_argument_mappings (arg_mappings, false);
1983+
1984+
infer->inherit_bounds ({pred});
1985+
DeferredOpOverload defer (expr.get_mappings ().get_hirid (),
1986+
lang_item_type, specified_segment, pred, expr);
1987+
context->insert_deferred_operator_overload (std::move (defer));
1988+
1989+
if (rhs != nullptr)
1990+
lhs = unify_site (expr.get_mappings ().get_hirid (),
1991+
TyTy::TyWithLocation (lhs),
1992+
TyTy::TyWithLocation (rhs), expr.get_locus ());
1993+
1994+
infered = unify_site (expr.get_mappings ().get_hirid (),
1995+
TyTy::TyWithLocation (lhs),
1996+
TyTy::TyWithLocation (infer), expr.get_locus ());
1997+
return true;
19411998
}
19421999

19432000
// Get the adjusted self

gcc/rust/typecheck/rust-hir-type-check-expr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class TypeCheckExpr : private TypeCheckBase, private HIR::HIRExpressionVisitor
3131
public:
3232
static TyTy::BaseType *Resolve (HIR::Expr &expr);
3333

34+
static TyTy::BaseType *
35+
ResolveOpOverload (LangItem::Kind lang_item_type, HIR::OperatorExprMeta expr,
36+
TyTy::BaseType *lhs, TyTy::BaseType *rhs,
37+
HIR::PathIdentSegment specified_segment);
38+
3439
void visit (HIR::TupleIndexExpr &expr) override;
3540
void visit (HIR::TupleExpr &expr) override;
3641
void visit (HIR::ReturnExpr &expr) override;

gcc/rust/typecheck/rust-hir-type-check.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define RUST_HIR_TYPE_CHECK
2121

2222
#include "rust-hir-map.h"
23+
#include "rust-mapping-common.h"
2324
#include "rust-tyty.h"
2425
#include "rust-hir-trait-reference.h"
2526
#include "rust-stacked-contexts.h"
@@ -157,6 +158,39 @@ class Lifetime
157158
WARN_UNUSED_RESULT Lifetime next () { return Lifetime (interner_index++); }
158159
};
159160

161+
struct DeferredOpOverload
162+
{
163+
HirId expr_id;
164+
LangItem::Kind lang_item_type;
165+
HIR::PathIdentSegment specified_segment;
166+
TyTy::TypeBoundPredicate predicate;
167+
HIR::OperatorExprMeta op;
168+
169+
DeferredOpOverload (HirId expr_id, LangItem::Kind lang_item_type,
170+
HIR::PathIdentSegment specified_segment,
171+
TyTy::TypeBoundPredicate &predicate,
172+
HIR::OperatorExprMeta op)
173+
: expr_id (expr_id), lang_item_type (lang_item_type),
174+
specified_segment (specified_segment), predicate (predicate), op (op)
175+
{}
176+
177+
DeferredOpOverload (const struct DeferredOpOverload &other)
178+
: expr_id (other.expr_id), lang_item_type (other.lang_item_type),
179+
specified_segment (other.specified_segment), predicate (other.predicate),
180+
op (other.op)
181+
{}
182+
183+
DeferredOpOverload &operator= (struct DeferredOpOverload const &other)
184+
{
185+
expr_id = other.expr_id;
186+
lang_item_type = other.lang_item_type;
187+
specified_segment = other.specified_segment;
188+
op = other.op;
189+
190+
return *this;
191+
}
192+
};
193+
160194
class TypeCheckContext
161195
{
162196
public:
@@ -237,6 +271,13 @@ class TypeCheckContext
237271
void insert_operator_overload (HirId id, TyTy::FnType *call_site);
238272
bool lookup_operator_overload (HirId id, TyTy::FnType **call);
239273

274+
void insert_deferred_operator_overload (DeferredOpOverload deferred);
275+
bool lookup_deferred_operator_overload (HirId id,
276+
DeferredOpOverload *deferred);
277+
278+
void iterate_deferred_operator_overloads (
279+
std::function<bool (HirId, DeferredOpOverload &)> cb);
280+
240281
void insert_unconstrained_check_marker (HirId id, bool status);
241282
bool have_checked_for_unconstrained (HirId id, bool *result);
242283

@@ -271,6 +312,7 @@ class TypeCheckContext
271312
TypeCheckContext ();
272313

273314
bool compute_infer_var (HirId id, TyTy::BaseType *ty, bool emit_error);
315+
bool compute_ambigious_op_overload (HirId id, DeferredOpOverload &op);
274316

275317
std::map<NodeId, HirId> node_id_refs;
276318
std::map<HirId, TyTy::BaseType *> resolved;
@@ -308,6 +350,9 @@ class TypeCheckContext
308350
std::set<HirId> querys_in_progress;
309351
std::set<DefId> trait_queries_in_progress;
310352

353+
// deferred operator overload
354+
std::map<HirId, DeferredOpOverload> deferred_operator_overloads;
355+
311356
// variance analysis
312357
TyTy::VarianceAnalysis::CrateCtx variance_analysis_ctx;
313358

0 commit comments

Comments
 (0)