Skip to content

Commit a9f047c

Browse files
committed
Auto merge of #46664 - mikeyhew:raw_pointer_self, r=arielb1
arbitrary_self_types: add support for raw pointer `self` types This adds support for raw pointer `self` types, under the `arbitrary_self_types` feature flag. Types like `self: *const Self`, `self: *const Rc<Self>`, `self: Rc<*const Self` are all supported. Object safety checks are updated to allow`self: *const Self` and `self: *mut Self`. This PR does not add support for `*const self` and `*mut self` syntax. That can be added in a later PR once this code is reviewed and merged. #44874 r? @arielb1
2 parents c8b94c6 + 5c656f0 commit a9f047c

13 files changed

+273
-33
lines changed

src/librustc/traits/object_safety.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ impl ObjectSafetyViolation {
5959
ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) =>
6060
format!("method `{}` has generic type parameters", name).into(),
6161
ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) =>
62-
format!("method `{}` has a non-standard `self` type. Only `&self`, \
63-
`&mut self`, and `Box<Self>` are currently supported \
64-
for trait objects", name).into(),
62+
format!("method `{}` has a non-standard `self` type", name).into(),
6563
ObjectSafetyViolation::AssociatedConst(name) =>
6664
format!("the trait cannot contain associated consts like `{}`", name).into(),
6765
}

src/librustc/ty/util.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
11911191
pub enum ExplicitSelf<'tcx> {
11921192
ByValue,
11931193
ByReference(ty::Region<'tcx>, hir::Mutability),
1194+
ByRawPointer(hir::Mutability),
11941195
ByBox,
11951196
Other
11961197
}
@@ -1231,10 +1232,15 @@ impl<'tcx> ExplicitSelf<'tcx> {
12311232

12321233
match self_arg_ty.sty {
12331234
_ if is_self_ty(self_arg_ty) => ByValue,
1234-
ty::TyRef(region, ty::TypeAndMut { ty, mutbl}) if is_self_ty(ty) => {
1235+
ty::TyRef(region, ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => {
12351236
ByReference(region, mutbl)
12361237
}
1237-
ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox,
1238+
ty::TyRawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => {
1239+
ByRawPointer(mutbl)
1240+
}
1241+
ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => {
1242+
ByBox
1243+
}
12381244
_ => Other
12391245
}
12401246
}

src/librustc_typeck/check/autoderef.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
3737
cur_ty: Ty<'tcx>,
3838
obligations: Vec<traits::PredicateObligation<'tcx>>,
3939
at_start: bool,
40+
include_raw_pointers: bool,
4041
span: Span,
4142
}
4243

@@ -76,12 +77,13 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
7677
}
7778

7879
// Otherwise, deref if type is derefable:
79-
let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
80-
(AutoderefKind::Builtin, mt.ty)
81-
} else {
82-
let ty = self.overloaded_deref_ty(self.cur_ty)?;
83-
(AutoderefKind::Overloaded, ty)
84-
};
80+
let (kind, new_ty) =
81+
if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers, NoPreference) {
82+
(AutoderefKind::Builtin, mt.ty)
83+
} else {
84+
let ty = self.overloaded_deref_ty(self.cur_ty)?;
85+
(AutoderefKind::Overloaded, ty)
86+
};
8587

8688
if new_ty.references_error() {
8789
return None;
@@ -194,6 +196,15 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
194196
}
195197
}
196198

199+
/// also dereference through raw pointer types
200+
/// e.g. assuming ptr_to_Foo is the type `*const Foo`
201+
/// fcx.autoderef(span, ptr_to_Foo) => [*const Foo]
202+
/// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
203+
pub fn include_raw_pointers(mut self) -> Self {
204+
self.include_raw_pointers = true;
205+
self
206+
}
207+
197208
pub fn finalize(self) {
198209
let fcx = self.fcx;
199210
fcx.register_predicates(self.into_obligations());
@@ -212,6 +223,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
212223
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
213224
obligations: vec![],
214225
at_start: true,
226+
include_raw_pointers: false,
215227
span,
216228
}
217229
}

src/librustc_typeck/check/method/confirm.rs

+1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
276276
// FIXME: this feels, like, super dubious
277277
self.fcx
278278
.autoderef(self.span, self_ty)
279+
.include_raw_pointers()
279280
.filter_map(|(ty, _)| {
280281
match ty.sty {
281282
ty::TyDynamic(ref data, ..) => data.principal().map(|p| closure(self, ty, p)),

src/librustc_typeck/check/method/probe.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> {
7777
struct CandidateStep<'tcx> {
7878
self_ty: Ty<'tcx>,
7979
autoderefs: usize,
80+
// true if the type results from a dereference of a raw pointer.
81+
// when assembling candidates, we include these steps, but not when
82+
// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
83+
// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
84+
// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
85+
from_unsafe_deref: bool,
8086
unsize: bool,
8187
}
8288

@@ -257,6 +263,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
257263
vec![CandidateStep {
258264
self_ty,
259265
autoderefs: 0,
266+
from_unsafe_deref: false,
260267
unsize: false,
261268
}]
262269
};
@@ -289,14 +296,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
289296
-> Option<Vec<CandidateStep<'tcx>>> {
290297
// FIXME: we don't need to create the entire steps in one pass
291298

292-
let mut autoderef = self.autoderef(span, self_ty);
299+
let mut autoderef = self.autoderef(span, self_ty).include_raw_pointers();
300+
let mut reached_raw_pointer = false;
293301
let mut steps: Vec<_> = autoderef.by_ref()
294302
.map(|(ty, d)| {
295-
CandidateStep {
303+
let step = CandidateStep {
296304
self_ty: ty,
297305
autoderefs: d,
306+
from_unsafe_deref: reached_raw_pointer,
298307
unsize: false,
308+
};
309+
if let ty::TyRawPtr(_) = ty.sty {
310+
// all the subsequent steps will be from_unsafe_deref
311+
reached_raw_pointer = true;
299312
}
313+
step
300314
})
301315
.collect();
302316

@@ -307,9 +321,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
307321
// a real method lookup, this is a hard error (it's an
308322
// ambiguity and we can't make progress).
309323
if !is_suggestion.0 {
310-
let t = self.structurally_resolved_type(span, final_ty);
311-
assert_eq!(t, self.tcx.types.err);
312-
return None
324+
if reached_raw_pointer
325+
&& !self.tcx.sess.features.borrow().arbitrary_self_types {
326+
// only produce a warning in this case, because inference variables used to
327+
// be allowed here in some cases for raw pointers
328+
struct_span_warn!(self.tcx.sess, span, E0619,
329+
"the type of this value must be known in this context")
330+
.note("this will be made into a hard error in a future version of \
331+
the compiler")
332+
.emit();
333+
} else {
334+
let t = self.structurally_resolved_type(span, final_ty);
335+
assert_eq!(t, self.tcx.types.err);
336+
return None
337+
}
313338
} else {
314339
// If we're just looking for suggestions,
315340
// though, ambiguity is no big thing, we can
@@ -322,6 +347,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
322347
steps.push(CandidateStep {
323348
self_ty: self.tcx.mk_slice(elem_ty),
324349
autoderefs: dereferences,
350+
// this could be from an unsafe deref if we had
351+
// a *mut/const [T; N]
352+
from_unsafe_deref: reached_raw_pointer,
325353
unsize: true,
326354
});
327355
}
@@ -830,7 +858,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
830858
.iter()
831859
.filter(|step| {
832860
debug!("pick_core: step={:?}", step);
833-
!step.self_ty.references_error()
861+
// skip types that are from a type error or that would require dereferencing
862+
// a raw pointer
863+
!step.self_ty.references_error() && !step.from_unsafe_deref
834864
}).flat_map(|step| {
835865
self.pick_by_value_method(step).or_else(|| {
836866
self.pick_autorefd_method(step, hir::MutImmutable).or_else(|| {

src/librustc_typeck/check/wfcheck.rs

+27-7
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
503503
&ty::Binder(self_arg_ty)
504504
);
505505

506-
let mut autoderef = fcx.autoderef(span, self_arg_ty);
506+
let mut autoderef = fcx.autoderef(span, self_arg_ty).include_raw_pointers();
507507

508508
loop {
509509
if let Some((potential_self_ty, _)) = autoderef.next() {
@@ -532,12 +532,32 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
532532
let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
533533
let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty);
534534

535-
if let ExplicitSelf::Other = self_kind {
536-
if !fcx.tcx.sess.features.borrow().arbitrary_self_types {
537-
feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span,
538-
GateIssue::Language, "arbitrary `self` types are unstable")
539-
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
540-
.emit();
535+
if !fcx.tcx.sess.features.borrow().arbitrary_self_types {
536+
match self_kind {
537+
ExplicitSelf::ByValue |
538+
ExplicitSelf::ByReference(_, _) |
539+
ExplicitSelf::ByBox => (),
540+
541+
ExplicitSelf::ByRawPointer(_) => {
542+
feature_gate::feature_err(
543+
&fcx.tcx.sess.parse_sess,
544+
"arbitrary_self_types",
545+
span,
546+
GateIssue::Language,
547+
"raw pointer `self` is unstable")
548+
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
549+
.emit();
550+
}
551+
552+
ExplicitSelf::Other => {
553+
feature_gate::feature_err(
554+
&fcx.tcx.sess.parse_sess,
555+
"arbitrary_self_types",
556+
span,
557+
GateIssue::Language,"arbitrary `self` types are unstable")
558+
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
559+
.emit();
560+
}
541561
}
542562
}
543563
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(arbitrary_self_types)]
12+
13+
use std::rc::Rc;
14+
15+
struct Foo(String);
16+
17+
impl Foo {
18+
unsafe fn foo(self: *const Self) -> *const str {
19+
(*self).0.as_ref()
20+
}
21+
22+
fn complicated_1(self: *const Rc<Self>) -> &'static str {
23+
"Foo::complicated_1"
24+
}
25+
26+
unsafe fn complicated_2(self: Rc<*const Self>) -> *const str {
27+
(**self).0.as_ref()
28+
}
29+
}
30+
31+
fn main() {
32+
let foo = Foo("abc123".into());
33+
assert_eq!("abc123", unsafe { &*(&foo as *const Foo).foo() });
34+
assert_eq!("Foo::complicated_1", std::ptr::null::<Rc<Foo>>().complicated_1());
35+
let rc = Rc::new(&foo as *const Foo);
36+
assert_eq!("abc123", unsafe { &*rc.complicated_2()});
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(arbitrary_self_types)]
12+
13+
use std::ptr;
14+
15+
trait Foo {
16+
fn foo(self: *const Self) -> &'static str;
17+
18+
unsafe fn bar(self: *const Self) -> i64;
19+
20+
unsafe fn complicated(self: *const *const Self) -> i64 where Self: Sized {
21+
(*self).bar()
22+
}
23+
}
24+
25+
impl Foo for i32 {
26+
fn foo(self: *const Self) -> &'static str {
27+
"I'm an i32!"
28+
}
29+
30+
unsafe fn bar(self: *const Self) -> i64 {
31+
*self as i64
32+
}
33+
}
34+
35+
impl Foo for u32 {
36+
fn foo(self: *const Self) -> &'static str {
37+
"I'm a u32!"
38+
}
39+
40+
unsafe fn bar(self: *const Self) -> i64 {
41+
*self as i64
42+
}
43+
}
44+
45+
fn main() {
46+
let null_i32 = ptr::null::<i32>() as *const Foo;
47+
let null_u32 = ptr::null::<u32>() as *const Foo;
48+
49+
assert_eq!("I'm an i32!", null_i32.foo());
50+
assert_eq!("I'm a u32!", null_u32.foo());
51+
52+
let valid_i32 = 5i32;
53+
let valid_i32_thin = &valid_i32 as *const i32;
54+
assert_eq!("I'm an i32!", valid_i32_thin.foo());
55+
assert_eq!(5, unsafe { valid_i32_thin.bar() });
56+
assert_eq!(5, unsafe { (&valid_i32_thin as *const *const i32).complicated() });
57+
let valid_i32_fat = valid_i32_thin as *const Foo;
58+
assert_eq!("I'm an i32!", valid_i32_fat.foo());
59+
assert_eq!(5, unsafe { valid_i32_fat.bar() });
60+
61+
let valid_u32 = 18u32;
62+
let valid_u32_thin = &valid_u32 as *const u32;
63+
assert_eq!("I'm a u32!", valid_u32_thin.foo());
64+
assert_eq!(18, unsafe { valid_u32_thin.bar() });
65+
assert_eq!(18, unsafe { (&valid_u32_thin as *const *const u32).complicated() });
66+
let valid_u32_fat = valid_u32_thin as *const Foo;
67+
assert_eq!("I'm a u32!", valid_u32_fat.foo());
68+
assert_eq!(18, unsafe { valid_u32_fat.bar() });
69+
70+
}

src/test/ui/arbitrary-self-types-not-object-safe.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ error[E0038]: the trait `Foo` cannot be made into an object
44
40 | let x = Box::new(5usize) as Box<Foo>;
55
| ^^^^^^^^ the trait `Foo` cannot be made into an object
66
|
7-
= note: method `foo` has a non-standard `self` type. Only `&self`, `&mut self`, and `Box<Self>` are currently supported for trait objects
7+
= note: method `foo` has a non-standard `self` type
88

99
error[E0038]: the trait `Foo` cannot be made into an object
1010
--> $DIR/arbitrary-self-types-not-object-safe.rs:40:13
1111
|
1212
40 | let x = Box::new(5usize) as Box<Foo>;
1313
| ^^^^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object
1414
|
15-
= note: method `foo` has a non-standard `self` type. Only `&self`, `&mut self`, and `Box<Self>` are currently supported for trait objects
15+
= note: method `foo` has a non-standard `self` type
1616
= note: required because of the requirements on the impl of `std::ops::CoerceUnsized<std::boxed::Box<Foo>>` for `std::boxed::Box<usize>`
1717

1818
error: aborting due to 2 previous errors
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo;
12+
13+
impl Foo {
14+
fn foo(self: *const Self) {}
15+
//~^ ERROR raw pointer `self` is unstable
16+
}
17+
18+
trait Bar {
19+
fn bar(self: *const Self);
20+
//~^ ERROR raw pointer `self` is unstable
21+
}
22+
23+
impl Bar for () {
24+
fn bar(self: *const Self) {}
25+
//~^ ERROR raw pointer `self` is unstable
26+
}
27+
28+
fn main() {}

0 commit comments

Comments
 (0)