Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement arbitrary_self_types #45870

Merged
merged 26 commits into from
Nov 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
edc8c76
add tests for the arbitrary_self_types, which won't pass yet
mikeyhew Oct 20, 2017
497397a
initial implementation of arbitrary_self_types
mikeyhew Nov 2, 2017
7dff08d
Rewrote check_method_receiver and ExplicitSelf, got a borrow checker …
mikeyhew Nov 5, 2017
a3f5866
Fix the lifetime error in ExplicitSelf
mikeyhew Nov 5, 2017
9a592e6
Improve feature gate error, and return after emitting errors instead …
mikeyhew Nov 5, 2017
d03eb2c
Fix some of the tests
mikeyhew Nov 5, 2017
aa0df3d
get the old error messages back
mikeyhew Nov 6, 2017
6fd2156
Add arbitrary_self_types feature gate error to some tests
mikeyhew Nov 7, 2017
0d739b5
Update/improve documentation of ExpliciSelf
mikeyhew Nov 7, 2017
1d29966
wrap error code in DiagnosticId::Error so it compiles
mikeyhew Nov 7, 2017
0b27dcc
modify ExplicitSelf::determine to take an `is_self_type` predicate cl…
mikeyhew Nov 8, 2017
3902643
move ExplicitSelf to rustc::ty::util, and use it to implement object …
mikeyhew Nov 8, 2017
2369746
normalize associated types in both self_ty and self_arg_ty
mikeyhew Nov 8, 2017
bbe755c
Switch from using At::eq to InferCtxt::can_eq and demand_eqtype_with_…
mikeyhew Nov 8, 2017
02ce3ac
add a NOTE comment to the object safety test so that it passes
mikeyhew Nov 8, 2017
e06cd31
Remove the error check that I think is redundant, and change the test…
mikeyhew Nov 8, 2017
0a3a46d
tidy things up a bit
mikeyhew Nov 8, 2017
0954e54
shorten line length for tidy
mikeyhew Nov 8, 2017
aa00f17
fix error message in arbitrary-self-types-not-object-safe test
mikeyhew Nov 8, 2017
7f8b003
update ui test to new error message
mikeyhew Nov 8, 2017
dcbb27a
fix the bug in region_inference where constraint origins were being o…
mikeyhew Nov 9, 2017
31d3783
fixed all the compile-fail error messages
mikeyhew Nov 9, 2017
c046fda
update test/ui/static-lifetime.stderr with new error message
mikeyhew Nov 9, 2017
5d170f0
add a comment explaining the bugfix to infer::region_inference::add_c…
mikeyhew Nov 9, 2017
ddc21d5
Don't emit the feature error if it's an invalid self type
mikeyhew Nov 9, 2017
77cd993
add run-pass tests
mikeyhew Nov 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/librustc/infer/region_inference/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,11 +633,15 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {

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

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

fn add_verify(&self, verify: Verify<'tcx>) {
Expand Down
17 changes: 16 additions & 1 deletion src/librustc/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use hir::def_id::DefId;
use traits;
use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::subst::Substs;
use ty::util::ExplicitSelf;
use std::borrow::Cow;
use syntax::ast;

Expand Down Expand Up @@ -57,6 +58,10 @@ impl ObjectSafetyViolation {
in its arguments or return type", name).into(),
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<Self>` are currently supported \
for trait objects", name).into(),
ObjectSafetyViolation::AssociatedConst(name) =>
format!("the trait cannot contain associated consts like `{}`", name).into(),
}
Expand All @@ -74,6 +79,9 @@ pub enum MethodViolationCode {

/// e.g., `fn foo<A>()`
Generic,

/// arbitrary `self` type, e.g. `self: Rc<Self>`
NonStandardSelfType,
}

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Expand Down Expand Up @@ -260,9 +268,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
return Some(MethodViolationCode::StaticMethod);
}

let sig = self.fn_sig(method.def_id);

let self_ty = self.mk_self_type();
let self_arg_ty = sig.skip_binder().inputs()[0];
if let ExplicitSelf::Other = ExplicitSelf::determine(self_arg_ty, |ty| ty == self_ty) {
return Some(MethodViolationCode::NonStandardSelfType);
}

// The `Self` type is erased, so it should not appear in list of
// arguments or return type apart from the receiver.
let ref sig = self.fn_sig(method.def_id);
for input_ty in &sig.skip_binder().inputs()[1..] {
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
return Some(MethodViolationCode::ReferencesSelf);
Expand Down
53 changes: 53 additions & 0 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use hir::def_id::{DefId, LOCAL_CRATE};
use hir::map::DefPathData;
use hir;
use ich::NodeIdHashingMode;
use middle::const_val::ConstVal;
use traits::{self, Reveal};
Expand Down Expand Up @@ -1178,6 +1179,58 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
layout
}

pub enum ExplicitSelf<'tcx> {
ByValue,
ByReference(ty::Region<'tcx>, hir::Mutability),
ByBox,
Other
}

impl<'tcx> ExplicitSelf<'tcx> {
/// Categorizes an explicit self declaration like `self: SomeType`
/// into either `self`, `&self`, `&mut self`, `Box<self>`, or
/// `Other`.
/// This is mainly used to require the arbitrary_self_types feature
/// in the case of `Other`, to improve error messages in the common cases,
/// and to make `Other` non-object-safe.
///
/// Examples:
///
/// ```
/// impl<'a> Foo for &'a T {
/// // Legal declarations:
/// fn method1(self: &&'a T); // ExplicitSelf::ByReference
/// fn method2(self: &'a T); // ExplicitSelf::ByValue
/// fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox
/// fn method4(self: Rc<&'a T>); // ExplicitSelf::Other
///
/// // Invalid cases will be caught by `check_method_receiver`:
/// fn method_err1(self: &'a mut T); // ExplicitSelf::Other
/// fn method_err2(self: &'static T) // ExplicitSelf::ByValue
/// fn method_err3(self: &&T) // ExplicitSelf::ByReference
/// }
/// ```
///
pub fn determine<P>(
self_arg_ty: Ty<'tcx>,
is_self_ty: P
) -> ExplicitSelf<'tcx>
where
P: Fn(Ty<'tcx>) -> bool
{
use self::ExplicitSelf::*;

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) => {
ByReference(region, mutbl)
}
ty::TyAdt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox,
_ => Other
}
}
}

pub fn provide(providers: &mut ty::maps::Providers) {
*providers = ty::maps::Providers {
is_copy_raw,
Expand Down
61 changes: 0 additions & 61 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,64 +1403,3 @@ impl<'a, 'gcx, 'tcx> Bounds<'tcx> {
vec
}
}

pub enum ExplicitSelf<'tcx> {
ByValue,
ByReference(ty::Region<'tcx>, hir::Mutability),
ByBox
}

impl<'tcx> ExplicitSelf<'tcx> {
/// We wish to (for now) categorize an explicit self
/// declaration like `self: SomeType` into either `self`,
/// `&self`, `&mut self`, or `Box<self>`. We do this here
/// by some simple pattern matching. A more precise check
/// is done later in `check_method_receiver()`.
///
/// Examples:
///
/// ```
/// impl Foo for &T {
/// // Legal declarations:
/// fn method1(self: &&T); // ExplicitSelf::ByReference
/// fn method2(self: &T); // ExplicitSelf::ByValue
/// fn method3(self: Box<&T>); // ExplicitSelf::ByBox
///
/// // Invalid cases will be caught later by `check_method_receiver`:
/// fn method_err1(self: &mut T); // ExplicitSelf::ByReference
/// }
/// ```
///
/// To do the check we just count the number of "modifiers"
/// on each type and compare them. If they are the same or
/// the impl has more, we call it "by value". Otherwise, we
/// look at the outermost modifier on the method decl and
/// call it by-ref, by-box as appropriate. For method1, for
/// example, the impl type has one modifier, but the method
/// type has two, so we end up with
/// ExplicitSelf::ByReference.
pub fn determine(untransformed_self_ty: Ty<'tcx>,
self_arg_ty: Ty<'tcx>)
-> ExplicitSelf<'tcx> {
fn count_modifiers(ty: Ty) -> usize {
match ty.sty {
ty::TyRef(_, mt) => count_modifiers(mt.ty) + 1,
ty::TyAdt(def, _) if def.is_box() => count_modifiers(ty.boxed_ty()) + 1,
_ => 0,
}
}

let impl_modifiers = count_modifiers(untransformed_self_ty);
let method_modifiers = count_modifiers(self_arg_ty);

if impl_modifiers >= method_modifiers {
ExplicitSelf::ByValue
} else {
match self_arg_ty.sty {
ty::TyRef(r, mt) => ExplicitSelf::ByReference(r, mt.mutbl),
ty::TyAdt(def, _) if def.is_box() => ExplicitSelf::ByBox,
_ => ExplicitSelf::ByValue,
}
}
}
}
19 changes: 12 additions & 7 deletions src/librustc_typeck/check/compare_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc::infer::{self, InferOk};
use rustc::middle::free_region::FreeRegionMap;
use rustc::middle::region;
use rustc::ty::{self, TyCtxt};
use rustc::ty::util::ExplicitSelf;
use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
use rustc::ty::error::{ExpectedFound, TypeError};
use rustc::ty::subst::{Subst, Substs};
Expand All @@ -21,7 +22,6 @@ use rustc::util::common::ErrorReported;
use syntax_pos::Span;

use super::{Inherited, FnCtxt};
use astconv::ExplicitSelf;

/// Checks that a method from an impl conforms to the signature of
/// the same method as declared in the trait.
Expand Down Expand Up @@ -503,12 +503,17 @@ fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty::TraitContainer(_) => tcx.mk_self_type()
};
let self_arg_ty = *tcx.fn_sig(method.def_id).input(0).skip_binder();
match ExplicitSelf::determine(untransformed_self_ty, self_arg_ty) {
ExplicitSelf::ByValue => "self".to_string(),
ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(),
ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(),
_ => format!("self: {}", self_arg_ty)
}
let param_env = ty::ParamEnv::empty(Reveal::All);

tcx.infer_ctxt().enter(|infcx| {
let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
ExplicitSelf::ByValue => "self".to_string(),
ExplicitSelf::ByReference(_, hir::MutImmutable) => "&self".to_string(),
ExplicitSelf::ByReference(_, hir::MutMutable) => "&mut self".to_string(),
_ => format!("self: {}", self_arg_ty)
}
})
};

match (trait_m.method_has_self_argument, impl_m.method_has_self_argument) {
Expand Down
71 changes: 51 additions & 20 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use astconv::ExplicitSelf;
use check::{Inherited, FnCtxt};
use constrained_type_params::{identify_constrained_type_params, Parameter};

use hir::def_id::DefId;
use rustc::traits::{self, ObligationCauseCode};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::util::ExplicitSelf;
use rustc::util::nodemap::{FxHashSet, FxHashMap};
use rustc::middle::lang_items;

use syntax::ast;
use syntax::feature_gate::{self, GateIssue};
use syntax_pos::Span;
use errors::DiagnosticBuilder;
use errors::{DiagnosticBuilder, DiagnosticId};

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

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

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

let self_ty = fcx.normalize_associated_types_in(span, &self_ty);
let self_ty = fcx.liberate_late_bound_regions(
method.def_id,
&ty::Binder(self_ty)
);

let self_arg_ty = sig.inputs()[0];
let rcvr_ty = match ExplicitSelf::determine(self_ty, self_arg_ty) {
ExplicitSelf::ByValue => self_ty,
ExplicitSelf::ByReference(region, mutbl) => {
fcx.tcx.mk_ref(region, ty::TypeAndMut {
ty: self_ty,
mutbl,
})

let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty);
let self_arg_ty = fcx.liberate_late_bound_regions(
method.def_id,
&ty::Binder(self_arg_ty)
);

let mut autoderef = fcx.autoderef(span, self_arg_ty);

loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
debug!("check_method_receiver: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty);

if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
autoderef.finalize();
if let Some(mut err) = fcx.demand_eqtype_with_origin(
&cause, self_ty, potential_self_ty) {
err.emit();
}
break
}
} else {
fcx.tcx.sess.diagnostic().mut_span_err(
span, &format!("invalid `self` type: {:?}", self_arg_ty))
.note(&format!("type must be `{:?}` or a type that dereferences to it`", self_ty))
.help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
.code(DiagnosticId::Error("E0307".into()))
.emit();
return
}
ExplicitSelf::ByBox => fcx.tcx.mk_box(self_ty)
};
let rcvr_ty = fcx.normalize_associated_types_in(span, &rcvr_ty);
let rcvr_ty = fcx.liberate_late_bound_regions(method.def_id,
&ty::Binder(rcvr_ty));
}

debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty);
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);

let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) {
err.emit();
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<Self>`")
.emit();
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4724,6 +4724,7 @@ register_diagnostics! {
// E0247,
// E0248, // value used as a type, now reported earlier during resolution as E0412
// E0249,
E0307, // invalid method `self` type
// E0319, // trait impls for defaulted traits allowed just for structs/enums
// E0372, // coherence not object safe
E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ declare_features! (

// extern types
(active, extern_types, "1.23.0", Some(43467)),

// Allow trait methods with arbitrary self types
(active, arbitrary_self_types, "1.23.0", Some(44874)),
);

declare_features! (
Expand Down
Loading