Skip to content

Commit b087ded

Browse files
committed
Auto merge of #45870 - mikeyhew:arbitrary_self_types, r=arielb1
Implement arbitrary_self_types r? @arielb1 cc @nikomatsakis Partial implementation of #44874. Supports trait and struct methods with arbitrary self types, as long as the type derefs (transitively) to `Self`. Doesn't support raw-pointer `self` yet. Methods with non-standard self types (i.e. anything other than `&self, &mut self, and Box<Self>`) are not object safe, because dynamic dispatch hasn't been implemented for them yet. I believe this is also a (partial) fix for #27941.
2 parents c1aacdc + 77cd993 commit b087ded

19 files changed

+349
-113
lines changed

src/librustc/infer/region_inference/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -633,11 +633,15 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
633633

634634
debug!("RegionVarBindings: add_constraint({:?})", constraint);
635635

636-
if self.constraints.borrow_mut().insert(constraint, origin).is_none() {
636+
// never overwrite an existing (constraint, origin) - only insert one if it isn't
637+
// present in the map yet. This prevents origins from outside the snapshot being
638+
// replaced with "less informative" origins e.g. during calls to `can_eq`
639+
self.constraints.borrow_mut().entry(constraint).or_insert_with(|| {
637640
if self.in_snapshot() {
638641
self.undo_log.borrow_mut().push(AddConstraint(constraint));
639642
}
640-
}
643+
origin
644+
});
641645
}
642646

643647
fn add_verify(&self, verify: Verify<'tcx>) {

src/librustc/traits/object_safety.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use hir::def_id::DefId;
2323
use traits;
2424
use ty::{self, Ty, TyCtxt, TypeFoldable};
2525
use ty::subst::Substs;
26+
use ty::util::ExplicitSelf;
2627
use std::borrow::Cow;
2728
use syntax::ast;
2829

@@ -57,6 +58,10 @@ impl ObjectSafetyViolation {
5758
in its arguments or return type", name).into(),
5859
ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) =>
5960
format!("method `{}` has generic type parameters", name).into(),
61+
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(),
6065
ObjectSafetyViolation::AssociatedConst(name) =>
6166
format!("the trait cannot contain associated consts like `{}`", name).into(),
6267
}
@@ -74,6 +79,9 @@ pub enum MethodViolationCode {
7479

7580
/// e.g., `fn foo<A>()`
7681
Generic,
82+
83+
/// arbitrary `self` type, e.g. `self: Rc<Self>`
84+
NonStandardSelfType,
7785
}
7886

7987
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
@@ -260,9 +268,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
260268
return Some(MethodViolationCode::StaticMethod);
261269
}
262270

271+
let sig = self.fn_sig(method.def_id);
272+
273+
let self_ty = self.mk_self_type();
274+
let self_arg_ty = sig.skip_binder().inputs()[0];
275+
if let ExplicitSelf::Other = ExplicitSelf::determine(self_arg_ty, |ty| ty == self_ty) {
276+
return Some(MethodViolationCode::NonStandardSelfType);
277+
}
278+
263279
// The `Self` type is erased, so it should not appear in list of
264280
// arguments or return type apart from the receiver.
265-
let ref sig = self.fn_sig(method.def_id);
266281
for input_ty in &sig.skip_binder().inputs()[1..] {
267282
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
268283
return Some(MethodViolationCode::ReferencesSelf);

src/librustc/ty/util.rs

+53
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
1313
use hir::def_id::{DefId, LOCAL_CRATE};
1414
use hir::map::DefPathData;
15+
use hir;
1516
use ich::NodeIdHashingMode;
1617
use middle::const_val::ConstVal;
1718
use traits::{self, Reveal};
@@ -1178,6 +1179,58 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
11781179
layout
11791180
}
11801181

1182+
pub enum ExplicitSelf<'tcx> {
1183+
ByValue,
1184+
ByReference(ty::Region<'tcx>, hir::Mutability),
1185+
ByBox,
1186+
Other
1187+
}
1188+
1189+
impl<'tcx> ExplicitSelf<'tcx> {
1190+
/// Categorizes an explicit self declaration like `self: SomeType`
1191+
/// into either `self`, `&self`, `&mut self`, `Box<self>`, or
1192+
/// `Other`.
1193+
/// This is mainly used to require the arbitrary_self_types feature
1194+
/// in the case of `Other`, to improve error messages in the common cases,
1195+
/// and to make `Other` non-object-safe.
1196+
///
1197+
/// Examples:
1198+
///
1199+
/// ```
1200+
/// impl<'a> Foo for &'a T {
1201+
/// // Legal declarations:
1202+
/// fn method1(self: &&'a T); // ExplicitSelf::ByReference
1203+
/// fn method2(self: &'a T); // ExplicitSelf::ByValue
1204+
/// fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox
1205+
/// fn method4(self: Rc<&'a T>); // ExplicitSelf::Other
1206+
///
1207+
/// // Invalid cases will be caught by `check_method_receiver`:
1208+
/// fn method_err1(self: &'a mut T); // ExplicitSelf::Other
1209+
/// fn method_err2(self: &'static T) // ExplicitSelf::ByValue
1210+
/// fn method_err3(self: &&T) // ExplicitSelf::ByReference
1211+
/// }
1212+
/// ```
1213+
///
1214+
pub fn determine<P>(
1215+
self_arg_ty: Ty<'tcx>,
1216+
is_self_ty: P
1217+
) -> ExplicitSelf<'tcx>
1218+
where
1219+
P: Fn(Ty<'tcx>) -> bool
1220+
{
1221+
use self::ExplicitSelf::*;
1222+
1223+
match self_arg_ty.sty {
1224+
_ if is_self_ty(self_arg_ty) => ByValue,
1225+
ty::TyRef(region, ty::TypeAndMut { ty, mutbl}) if is_self_ty(ty) => {
1226+
ByReference(region, mutbl)
1227+
}
1228+
ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox,
1229+
_ => Other
1230+
}
1231+
}
1232+
}
1233+
11811234
pub fn provide(providers: &mut ty::maps::Providers) {
11821235
*providers = ty::maps::Providers {
11831236
is_copy_raw,

src/librustc_typeck/astconv.rs

-61
Original file line numberDiff line numberDiff line change
@@ -1386,64 +1386,3 @@ impl<'a, 'gcx, 'tcx> Bounds<'tcx> {
13861386
vec
13871387
}
13881388
}
1389-
1390-
pub enum ExplicitSelf<'tcx> {
1391-
ByValue,
1392-
ByReference(ty::Region<'tcx>, hir::Mutability),
1393-
ByBox
1394-
}
1395-
1396-
impl<'tcx> ExplicitSelf<'tcx> {
1397-
/// We wish to (for now) categorize an explicit self
1398-
/// declaration like `self: SomeType` into either `self`,
1399-
/// `&self`, `&mut self`, or `Box<self>`. We do this here
1400-
/// by some simple pattern matching. A more precise check
1401-
/// is done later in `check_method_receiver()`.
1402-
///
1403-
/// Examples:
1404-
///
1405-
/// ```
1406-
/// impl Foo for &T {
1407-
/// // Legal declarations:
1408-
/// fn method1(self: &&T); // ExplicitSelf::ByReference
1409-
/// fn method2(self: &T); // ExplicitSelf::ByValue
1410-
/// fn method3(self: Box<&T>); // ExplicitSelf::ByBox
1411-
///
1412-
/// // Invalid cases will be caught later by `check_method_receiver`:
1413-
/// fn method_err1(self: &mut T); // ExplicitSelf::ByReference
1414-
/// }
1415-
/// ```
1416-
///
1417-
/// To do the check we just count the number of "modifiers"
1418-
/// on each type and compare them. If they are the same or
1419-
/// the impl has more, we call it "by value". Otherwise, we
1420-
/// look at the outermost modifier on the method decl and
1421-
/// call it by-ref, by-box as appropriate. For method1, for
1422-
/// example, the impl type has one modifier, but the method
1423-
/// type has two, so we end up with
1424-
/// ExplicitSelf::ByReference.
1425-
pub fn determine(untransformed_self_ty: Ty<'tcx>,
1426-
self_arg_ty: Ty<'tcx>)
1427-
-> ExplicitSelf<'tcx> {
1428-
fn count_modifiers(ty: Ty) -> usize {
1429-
match ty.sty {
1430-
ty::TyRef(_, mt) => count_modifiers(mt.ty) + 1,
1431-
ty::TyAdt(def, _) if def.is_box() => count_modifiers(ty.boxed_ty()) + 1,
1432-
_ => 0,
1433-
}
1434-
}
1435-
1436-
let impl_modifiers = count_modifiers(untransformed_self_ty);
1437-
let method_modifiers = count_modifiers(self_arg_ty);
1438-
1439-
if impl_modifiers >= method_modifiers {
1440-
ExplicitSelf::ByValue
1441-
} else {
1442-
match self_arg_ty.sty {
1443-
ty::TyRef(r, mt) => ExplicitSelf::ByReference(r, mt.mutbl),
1444-
ty::TyAdt(def, _) if def.is_box() => ExplicitSelf::ByBox,
1445-
_ => ExplicitSelf::ByValue,
1446-
}
1447-
}
1448-
}
1449-
}

src/librustc_typeck/check/compare_method.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc::infer::{self, InferOk};
1313
use rustc::middle::free_region::FreeRegionMap;
1414
use rustc::middle::region;
1515
use rustc::ty::{self, TyCtxt};
16+
use rustc::ty::util::ExplicitSelf;
1617
use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
1718
use rustc::ty::error::{ExpectedFound, TypeError};
1819
use rustc::ty::subst::{Subst, Substs};
@@ -21,7 +22,6 @@ use rustc::util::common::ErrorReported;
2122
use syntax_pos::Span;
2223

2324
use super::{Inherited, FnCtxt};
24-
use astconv::ExplicitSelf;
2525

2626
/// Checks that a method from an impl conforms to the signature of
2727
/// the same method as declared in the trait.
@@ -503,12 +503,17 @@ fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
503503
ty::TraitContainer(_) => tcx.mk_self_type()
504504
};
505505
let self_arg_ty = *tcx.fn_sig(method.def_id).input(0).skip_binder();
506-
match ExplicitSelf::determine(untransformed_self_ty, self_arg_ty) {
507-
ExplicitSelf::ByValue => "self".to_string(),
508-
ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(),
509-
ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(),
510-
_ => format!("self: {}", self_arg_ty)
511-
}
506+
let param_env = ty::ParamEnv::empty(Reveal::All);
507+
508+
tcx.infer_ctxt().enter(|infcx| {
509+
let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
510+
match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
511+
ExplicitSelf::ByValue => "self".to_string(),
512+
ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(),
513+
ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(),
514+
_ => format!("self: {}", self_arg_ty)
515+
}
516+
})
512517
};
513518

514519
match (trait_m.method_has_self_argument, impl_m.method_has_self_argument) {

src/librustc_typeck/check/wfcheck.rs

+51-20
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use astconv::ExplicitSelf;
1211
use check::{Inherited, FnCtxt};
1312
use constrained_type_params::{identify_constrained_type_params, Parameter};
1413

1514
use hir::def_id::DefId;
1615
use rustc::traits::{self, ObligationCauseCode};
1716
use rustc::ty::{self, Ty, TyCtxt};
17+
use rustc::ty::util::ExplicitSelf;
1818
use rustc::util::nodemap::{FxHashSet, FxHashMap};
1919
use rustc::middle::lang_items;
2020

2121
use syntax::ast;
22+
use syntax::feature_gate::{self, GateIssue};
2223
use syntax_pos::Span;
23-
use errors::DiagnosticBuilder;
24+
use errors::{DiagnosticBuilder, DiagnosticId};
2425

2526
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
2627
use rustc::hir;
@@ -451,8 +452,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
451452
method: &ty::AssociatedItem,
452453
self_ty: Ty<'tcx>)
453454
{
454-
// check that the type of the method's receiver matches the
455-
// method's first parameter.
455+
// check that the method has a valid receiver type, given the type `Self`
456456
debug!("check_method_receiver({:?}, self_ty={:?})",
457457
method, self_ty);
458458

@@ -468,26 +468,57 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
468468

469469
debug!("check_method_receiver: sig={:?}", sig);
470470

471+
let self_ty = fcx.normalize_associated_types_in(span, &self_ty);
472+
let self_ty = fcx.liberate_late_bound_regions(
473+
method.def_id,
474+
&ty::Binder(self_ty)
475+
);
476+
471477
let self_arg_ty = sig.inputs()[0];
472-
let rcvr_ty = match ExplicitSelf::determine(self_ty, self_arg_ty) {
473-
ExplicitSelf::ByValue => self_ty,
474-
ExplicitSelf::ByReference(region, mutbl) => {
475-
fcx.tcx.mk_ref(region, ty::TypeAndMut {
476-
ty: self_ty,
477-
mutbl,
478-
})
478+
479+
let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
480+
let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty);
481+
let self_arg_ty = fcx.liberate_late_bound_regions(
482+
method.def_id,
483+
&ty::Binder(self_arg_ty)
484+
);
485+
486+
let mut autoderef = fcx.autoderef(span, self_arg_ty);
487+
488+
loop {
489+
if let Some((potential_self_ty, _)) = autoderef.next() {
490+
debug!("check_method_receiver: potential self type `{:?}` to match `{:?}`",
491+
potential_self_ty, self_ty);
492+
493+
if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
494+
autoderef.finalize();
495+
if let Some(mut err) = fcx.demand_eqtype_with_origin(
496+
&cause, self_ty, potential_self_ty) {
497+
err.emit();
498+
}
499+
break
500+
}
501+
} else {
502+
fcx.tcx.sess.diagnostic().mut_span_err(
503+
span, &format!("invalid `self` type: {:?}", self_arg_ty))
504+
.note(&format!("type must be `{:?}` or a type that dereferences to it`", self_ty))
505+
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
506+
.code(DiagnosticId::Error("E0307".into()))
507+
.emit();
508+
return
479509
}
480-
ExplicitSelf::ByBox => fcx.tcx.mk_box(self_ty)
481-
};
482-
let rcvr_ty = fcx.normalize_associated_types_in(span, &rcvr_ty);
483-
let rcvr_ty = fcx.liberate_late_bound_regions(method.def_id,
484-
&ty::Binder(rcvr_ty));
510+
}
485511

486-
debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty);
512+
let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
513+
let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty);
487514

488-
let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
489-
if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) {
490-
err.emit();
515+
if let ExplicitSelf::Other = self_kind {
516+
if !fcx.tcx.sess.features.borrow().arbitrary_self_types {
517+
feature_gate::feature_err(&fcx.tcx.sess.parse_sess, "arbitrary_self_types", span,
518+
GateIssue::Language, "arbitrary `self` types are unstable")
519+
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
520+
.emit();
521+
}
491522
}
492523
}
493524

src/librustc_typeck/diagnostics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4724,6 +4724,7 @@ register_diagnostics! {
47244724
// E0247,
47254725
// E0248, // value used as a type, now reported earlier during resolution as E0412
47264726
// E0249,
4727+
E0307, // invalid method `self` type
47274728
// E0319, // trait impls for defaulted traits allowed just for structs/enums
47284729
// E0372, // coherence not object safe
47294730
E0377, // the trait `CoerceUnsized` may only be implemented for a coercion

src/libsyntax/feature_gate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ declare_features! (
409409

410410
// extern types
411411
(active, extern_types, "1.23.0", Some(43467)),
412+
413+
// Allow trait methods with arbitrary self types
414+
(active, arbitrary_self_types, "1.23.0", Some(44874)),
412415
);
413416

414417
declare_features! (

0 commit comments

Comments
 (0)