From 361b3db886c6f63c57c11e036b01bb8b09f35739 Mon Sep 17 00:00:00 2001 From: Michael Hewson Date: Thu, 9 Nov 2017 16:15:35 -0500 Subject: [PATCH] implement raw-pointer `self`. Works for traits, including trait objects, but not structs --- src/librustc/traits/object_safety.rs | 4 +- src/librustc/ty/util.rs | 10 +++- src/librustc_typeck/check/autoderef.rs | 24 ++++++-- src/librustc_typeck/check/wfcheck.rs | 27 ++++++--- ...arbitrary_self_types_raw_pointer_struct.rs | 24 ++++++++ .../arbitrary_self_types_raw_pointer_trait.rs | 59 +++++++++++++++++++ 6 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 src/test/run-pass/arbitrary_self_types_raw_pointer_struct.rs create mode 100644 src/test/run-pass/arbitrary_self_types_raw_pointer_trait.rs diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index facd6350e196..4151661b5933 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -59,9 +59,7 @@ impl ObjectSafetyViolation { ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) => format!("method `{}` has generic type parameters", name).into(), ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) => - format!("method `{}` has a non-standard `self` type. Only `&self`, \ - `&mut self`, and `Box` are currently supported \ - for trait objects", name).into(), + format!("method `{}` has a non-standard `self` type", name).into(), ObjectSafetyViolation::AssociatedConst(name) => format!("the trait cannot contain associated consts like `{}`", name).into(), } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2e9e45c9ffe1..46bc54b7f6a6 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -1191,6 +1191,7 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub enum ExplicitSelf<'tcx> { ByValue, ByReference(ty::Region<'tcx>, hir::Mutability), + ByRawPointer(hir::Mutability), ByBox, Other } @@ -1231,10 +1232,15 @@ impl<'tcx> ExplicitSelf<'tcx> { match self_arg_ty.sty { _ if is_self_ty(self_arg_ty) => ByValue, - ty::TyRef(region, ty::TypeAndMut { ty, mutbl}) if is_self_ty(ty) => { + ty::TyRef(region, ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => { ByReference(region, mutbl) } - ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, + ty::TyRawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => { + ByRawPointer(mutbl) + } + ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => { + ByBox + } _ => Other } } diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index d075bf436792..7832db7dcb54 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -37,6 +37,7 @@ pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> { cur_ty: Ty<'tcx>, obligations: Vec>, at_start: bool, + include_raw_pointers: bool, span: Span, } @@ -76,12 +77,13 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> { } // Otherwise, deref if type is derefable: - let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) { - (AutoderefKind::Builtin, mt.ty) - } else { - let ty = self.overloaded_deref_ty(self.cur_ty)?; - (AutoderefKind::Overloaded, ty) - }; + let (kind, new_ty) = + if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers, NoPreference) { + (AutoderefKind::Builtin, mt.ty) + } else { + let ty = self.overloaded_deref_ty(self.cur_ty)?; + (AutoderefKind::Overloaded, ty) + }; if new_ty.references_error() { return None; @@ -194,6 +196,15 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { } } + /// also dereference through raw pointer types + /// e.g. assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + pub fn finalize(self) { let fcx = self.fcx; fcx.register_predicates(self.into_obligations()); @@ -212,6 +223,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { cur_ty: self.resolve_type_vars_if_possible(&base_ty), obligations: vec![], at_start: true, + include_raw_pointers: false, span, } } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index d4625bb58c33..5cb6c6a368e1 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -503,7 +503,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { &ty::Binder(self_arg_ty) ); - let mut autoderef = fcx.autoderef(span, self_arg_ty); + let mut autoderef = fcx.autoderef(span, self_arg_ty).include_raw_pointers(); loop { if let Some((potential_self_ty, _)) = autoderef.next() { @@ -532,12 +532,25 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok(); let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty); - if let ExplicitSelf::Other = self_kind { - if !fcx.tcx.sess.features.borrow().arbitrary_self_types { - feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span, - GateIssue::Language, "arbitrary `self` types are unstable") - .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box`") - .emit(); + if !fcx.tcx.sess.features.borrow().arbitrary_self_types { + match self_kind { + ExplicitSelf::ByValue | + ExplicitSelf::ByReference(_, _) | + ExplicitSelf::ByBox => (), + + ExplicitSelf::ByRawPointer(_) => { + feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span, + GateIssue::Language, "raw pointer `self` is unstable") + .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box`") + .emit(); + } + + ExplicitSelf::Other => { + feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span, + GateIssue::Language, "arbitrary `self` types are unstable") + .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box`") + .emit(); + } } } } diff --git a/src/test/run-pass/arbitrary_self_types_raw_pointer_struct.rs b/src/test/run-pass/arbitrary_self_types_raw_pointer_struct.rs new file mode 100644 index 000000000000..e213d33a9c2d --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_raw_pointer_struct.rs @@ -0,0 +1,24 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(arbitrary_self_types)] + +struct Foo(String); + +impl Foo { + unsafe fn foo(self: *const Self) -> *const str { + (*self).0.as_ref() + } +} + +fn main() { + let foo = Foo("abc123".into()); + assert_eq!("abc123", unsafe { &*(&foo as *const Foo).foo() }); +} diff --git a/src/test/run-pass/arbitrary_self_types_raw_pointer_trait.rs b/src/test/run-pass/arbitrary_self_types_raw_pointer_trait.rs new file mode 100644 index 000000000000..0d64dacaf4e7 --- /dev/null +++ b/src/test/run-pass/arbitrary_self_types_raw_pointer_trait.rs @@ -0,0 +1,59 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(arbitrary_self_types)] + +use std::ptr; + +trait Foo { + fn foo(self: *const Self) -> &'static str; + + unsafe fn bar(self: *const Self) -> i64; +} + +impl Foo for i32 { + fn foo(self: *const Self) -> &'static str { + "I'm an i32!" + } + + unsafe fn bar(self: *const Self) -> i64 { + *self as i64 + } +} + +impl Foo for u32 { + fn foo(self: *const Self) -> &'static str { + "I'm a u32!" + } + + unsafe fn bar(self: *const Self) -> i64 { + *self as i64 + } +} + +fn main() { + let foo_i32 = ptr::null::() as *const Foo; + let foo_u32 = ptr::null::() as *const Foo; + + assert_eq!("I'm an i32!", foo_i32.foo()); + assert_eq!("I'm a u32!", foo_u32.foo()); + + let bar_i32 = 5i32; + let bar_i32_thin = &bar_i32 as *const i32; + assert_eq!(5, unsafe { bar_i32_thin.bar() }); + let bar_i32_fat = bar_i32_thin as *const Foo; + assert_eq!(5, unsafe { bar_i32_fat.bar() }); + + let bar_u32 = 18u32; + let bar_u32_thin = &bar_u32 as *const u32; + assert_eq!(18, unsafe { bar_u32_thin.bar() }); + let bar_u32_fat = bar_u32_thin as *const Foo; + assert_eq!(18, unsafe { bar_u32_fat.bar() }); +}