aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-11-24 18:45:34 +0000
committerPhilip Herron <philip.herron@embecosm.com>2021-11-26 09:14:10 +0000
commitc7af812802ff64a88b4d251dae83377f45c0a8a3 (patch)
tree8996e916a4375af6f33d1a835218a0e1fb003fd6 /gcc
parent3b8cbff816c969aa43aca9c611b3c3f9ef5a1af5 (diff)
downloadgcc-c7af812802ff64a88b4d251dae83377f45c0a8a3.zip
gcc-c7af812802ff64a88b4d251dae83377f45c0a8a3.tar.gz
gcc-c7af812802ff64a88b4d251dae83377f45c0a8a3.tar.bz2
Support dereference operator overloading
This adds in support for deref lang-item operator overloads. Deref operator overloading is an interesting case of the libcore interaction with the compiler. The deref operator lang item is: ```rust pub trait Deref { type Target; fn deref(&self) -> &Self::Target; } ``` It has two default impl's one for '&T' and '&mut T' to apply genericly. The reason it is interesting is from the prototype the deref lang item always returns &Self::Target in all cases regardless of mutability, the lang item here is designed to wrap up any dereference such that when applied it guarentees the type system you will get back an immutable reference to something. The reason for doing this is more clear when thinking about autoderef and method-resolution and how you apply dereference operations to custom types and a test case is included for that. The autoderef mechanism will now need to be updated to support drefs fully. Fixes #809
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/backend/rust-compile-expr.cc37
-rw-r--r--gcc/rust/backend/rust-compile-expr.h21
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-expr.h33
-rw-r--r--gcc/rust/util/rust-hir-map.h8
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_10.rs79
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_7.rs44
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_8.rs61
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_9.rs61
8 files changed, 321 insertions, 23 deletions
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index fb01d8d..6051f50 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -117,6 +117,43 @@ CompileExpr::visit (HIR::NegationExpr &expr)
= ctx->get_backend ()->negation_expression (op, negated_expr, location);
}
+void
+CompileExpr::visit (HIR::DereferenceExpr &expr)
+{
+ TyTy::BaseType *tyty = nullptr;
+ if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+ &tyty))
+ {
+ rust_fatal_error (expr.get_locus (),
+ "did not resolve type for this TupleExpr");
+ return;
+ }
+
+ tree main_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
+
+ // this might be an operator overload situation lets check
+ TyTy::FnType *fntype;
+ bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+ expr.get_mappings ().get_hirid (), &fntype);
+ if (is_op_overload)
+ {
+ auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
+ tree operator_overload_call
+ = resolve_operator_overload (lang_item_type, expr, main_expr, nullptr,
+ expr.get_expr ().get (), nullptr);
+
+ // rust deref always returns a reference from this overload then we can
+ // actually do the indirection
+ main_expr = operator_overload_call;
+ }
+
+ tree expected_type = TyTyResolveCompile::compile (ctx, tyty);
+ bool known_valid = true;
+ translated
+ = ctx->get_backend ()->indirect_expression (expected_type, main_expr,
+ known_valid, expr.get_locus ());
+}
+
tree
CompileExpr::compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
TyTy::BaseType *receiver,
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 46d501a..1e8e1f6 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -979,26 +979,7 @@ public:
= ctx->get_backend ()->address_expression (main_expr, expr.get_locus ());
}
- void visit (HIR::DereferenceExpr &expr) override
- {
- tree main_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
-
- TyTy::BaseType *tyty = nullptr;
- if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
- &tyty))
- {
- rust_fatal_error (expr.get_locus (),
- "did not resolve type for this TupleExpr");
- return;
- }
-
- tree expected_type = TyTyResolveCompile::compile (ctx, tyty);
- bool known_valid = true;
- translated
- = ctx->get_backend ()->indirect_expression (expected_type, main_expr,
- known_valid,
- expr.get_locus ());
- }
+ void visit (HIR::DereferenceExpr &expr) override;
protected:
tree compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
index 5d6017c..1015cc2 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -1219,6 +1219,18 @@ public:
TyTy::BaseType *resolved_base
= TypeCheckExpr::Resolve (expr.get_expr ().get (), false);
+ auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
+ bool operator_overloaded
+ = resolve_operator_overload (lang_item_type, expr, resolved_base,
+ nullptr);
+ if (operator_overloaded)
+ {
+ // operator overloaded deref always refurns a reference type lets assert
+ // this
+ rust_assert (infered->get_kind () == TyTy::TypeKind::REF);
+ resolved_base = infered;
+ }
+
bool is_valid_type
= resolved_base->get_kind () == TyTy::TypeKind::REF
|| resolved_base->get_kind () == TyTy::TypeKind::POINTER;
@@ -1281,9 +1293,24 @@ protected:
bool probe_impls = !receiver_is_generic;
bool ignore_mandatory_trait_items = !receiver_is_generic;
- auto candidates = PathProbeType::Probe (
- root, HIR::PathIdentSegment (associated_item_name), probe_impls,
- probe_bounds, ignore_mandatory_trait_items, respective_lang_item_id);
+ auto probe_type = probe_impls ? lhs : root;
+ auto candidates
+ = PathProbeType::Probe (probe_type,
+ HIR::PathIdentSegment (associated_item_name),
+ probe_impls, probe_bounds,
+ ignore_mandatory_trait_items);
+ if (candidates.empty ())
+ {
+ if (probe_impls)
+ {
+ candidates = PathProbeType::Probe (
+ root, HIR::PathIdentSegment (associated_item_name), probe_impls,
+ probe_bounds, ignore_mandatory_trait_items);
+ }
+
+ if (candidates.empty ())
+ return false;
+ }
// autoderef to find the relevant method
std::vector<Adjustment> adjustments;
diff --git a/gcc/rust/util/rust-hir-map.h b/gcc/rust/util/rust-hir-map.h
index 4f8c389..77a4caf 100644
--- a/gcc/rust/util/rust-hir-map.h
+++ b/gcc/rust/util/rust-hir-map.h
@@ -51,6 +51,8 @@ public:
DIV_ASSIGN,
REM_ASSIGN,
+ DEREF,
+
UNKNOWN,
};
@@ -104,6 +106,10 @@ public:
{
return ItemType::REM_ASSIGN;
}
+ else if (item.compare ("deref") == 0)
+ {
+ return ItemType::DEREF;
+ }
return ItemType::UNKNOWN;
}
@@ -136,6 +142,8 @@ public:
return "div_assign";
case REM_ASSIGN:
return "rem_assign";
+ case DEREF:
+ return "deref";
case UNKNOWN:
break;
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_10.rs b/gcc/testsuite/rust/execute/torture/operator_overload_10.rs
new file mode 100644
index 0000000..23ae778
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_10.rs
@@ -0,0 +1,79 @@
+/* { dg-output "foo_deref\n123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target;
+
+ fn deref(&self) -> &Self::Target;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .Deref::deref." "" { target *-*-* } .-2 }
+}
+
+impl<T> Deref for &T {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ 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 {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ 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
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_7.rs b/gcc/testsuite/rust/execute/torture/operator_overload_7.rs
new file mode 100644
index 0000000..6ff3a87
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_7.rs
@@ -0,0 +1,44 @@
+/* { dg-output "imm_deref\n123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target;
+
+ fn deref(&self) -> &Self::Target;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .Deref::deref." "" { target *-*-* } .-2 }
+}
+
+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
+ }
+}
+
+fn main() -> i32 {
+ let foo: &i32 = &123;
+ let res: i32 = *foo;
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res);
+ }
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_8.rs b/gcc/testsuite/rust/execute/torture/operator_overload_8.rs
new file mode 100644
index 0000000..d853d71
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_8.rs
@@ -0,0 +1,61 @@
+/* { dg-output "imm_deref\n123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target;
+
+ fn deref(&self) -> &Self::Target;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .Deref::deref." "" { target *-*-* } .-2 }
+}
+
+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 {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ unsafe {
+ let a = "mut_deref\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c);
+ }
+
+ *self
+ }
+}
+
+fn main() -> i32 {
+ let foo: &i32 = &123;
+ let res: i32 = *foo;
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res);
+ }
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_9.rs b/gcc/testsuite/rust/execute/torture/operator_overload_9.rs
new file mode 100644
index 0000000..5c770e7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_9.rs
@@ -0,0 +1,61 @@
+/* { dg-output "mut_deref\n123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target;
+
+ fn deref(&self) -> &Self::Target;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .Deref::deref." "" { target *-*-* } .-2 }
+}
+
+impl<T> Deref for &T {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ 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
+ }
+}
+
+fn main() -> i32 {
+ let foo = &mut 123;
+ let res: i32 = *foo;
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res);
+ }
+
+ 0
+}