Skip to content

Commit fdd9cdc

Browse files
committed
Auto merge of #50966 - leodasvacas:self-in-where-clauses-is-not-object-safe, r=nikomatsakis
`Self` in where clauses may not be object safe Needs crater, virtually certain to cause regressions. In #50781 it was discovered that our object safety rules are not sound because we allow `Self` in where clauses without restrain. This PR is a direct fix to the rules so that we disallow methods with unsound where clauses. This currently uses hard error to measure impact, but we will want to downgrade it to a future compat error. Part of #50781. r? @nikomatsakis
2 parents 2a1c4ee + cc60e01 commit fdd9cdc

File tree

9 files changed

+115
-41
lines changed

9 files changed

+115
-41
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,12 @@ declare_lint! {
304304
"warn about documentation intra links resolution failure"
305305
}
306306

307+
declare_lint! {
308+
pub WHERE_CLAUSES_OBJECT_SAFETY,
309+
Warn,
310+
"checks the object safety of where clauses"
311+
}
312+
307313
/// Does nothing as a lint pass, but registers some `Lint`s
308314
/// which are used by other parts of the compiler.
309315
#[derive(Copy, Clone)]
@@ -358,6 +364,7 @@ impl LintPass for HardwiredLints {
358364
DUPLICATE_ASSOCIATED_TYPE_BINDINGS,
359365
DUPLICATE_MACRO_EXPORTS,
360366
INTRA_DOC_LINK_RESOLUTION_FAILURE,
367+
WHERE_CLAUSES_OBJECT_SAFETY,
361368
)
362369
}
363370
}

src/librustc/traits/object_safety.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
use super::elaborate_predicates;
2121

2222
use hir::def_id::DefId;
23+
use lint;
2324
use traits;
2425
use ty::{self, Ty, TyCtxt, TypeFoldable};
2526
use ty::subst::Substs;
2627
use ty::util::ExplicitSelf;
2728
use std::borrow::Cow;
2829
use syntax::ast;
30+
use syntax_pos::Span;
2931

30-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
32+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
3133
pub enum ObjectSafetyViolation {
3234
/// Self : Sized declared on the trait
3335
SizedSelf,
@@ -56,6 +58,9 @@ impl ObjectSafetyViolation {
5658
ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelf) =>
5759
format!("method `{}` references the `Self` type \
5860
in its arguments or return type", name).into(),
61+
ObjectSafetyViolation::Method(name,
62+
MethodViolationCode::WhereClauseReferencesSelf(_)) =>
63+
format!("method `{}` references the `Self` type in where clauses", name).into(),
5964
ObjectSafetyViolation::Method(name, MethodViolationCode::Generic) =>
6065
format!("method `{}` has generic type parameters", name).into(),
6166
ObjectSafetyViolation::Method(name, MethodViolationCode::NonStandardSelfType) =>
@@ -75,6 +80,9 @@ pub enum MethodViolationCode {
7580
/// e.g., `fn foo(&self, x: Self)` or `fn foo(&self) -> Self`
7681
ReferencesSelf,
7782

83+
/// e.g. `fn foo(&self) where Self: Clone`
84+
WhereClauseReferencesSelf(Span),
85+
7886
/// e.g., `fn foo<A>()`
7987
Generic,
8088

@@ -123,6 +131,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
123131
.filter_map(|item| {
124132
self.object_safety_violation_for_method(trait_def_id, &item)
125133
.map(|code| ObjectSafetyViolation::Method(item.name, code))
134+
}).filter(|violation| {
135+
if let ObjectSafetyViolation::Method(_,
136+
MethodViolationCode::WhereClauseReferencesSelf(span)) = violation {
137+
// Using`CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
138+
// It's also hard to get a use site span, so we use the method definition span.
139+
self.lint_node_note(
140+
lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY,
141+
ast::CRATE_NODE_ID,
142+
*span,
143+
&format!("the trait `{}` cannot be made into an object",
144+
self.item_path_str(trait_def_id)),
145+
&violation.error_msg());
146+
false
147+
} else {
148+
true
149+
}
126150
}).collect();
127151

128152
// Check the trait itself.
@@ -245,7 +269,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
245269
return false;
246270
}
247271

248-
self.virtual_call_violation_for_method(trait_def_id, method).is_none()
272+
match self.virtual_call_violation_for_method(trait_def_id, method) {
273+
None | Some(MethodViolationCode::WhereClauseReferencesSelf(_)) => true,
274+
Some(_) => false,
275+
}
249276
}
250277

251278
/// Returns `Some(_)` if this method cannot be called on a trait
@@ -288,6 +315,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
288315
return Some(MethodViolationCode::Generic);
289316
}
290317

318+
if self.predicates_of(method.def_id).predicates.into_iter()
319+
// A trait object can't claim to live more than the concrete type,
320+
// so outlives predicates will always hold.
321+
.filter(|p| p.to_opt_type_outlives().is_none())
322+
.collect::<Vec<_>>()
323+
// Do a shallow visit so that `contains_illegal_self_type_reference`
324+
// may apply it's custom visiting.
325+
.visit_tys_shallow(|t| self.contains_illegal_self_type_reference(trait_def_id, t)) {
326+
let span = self.def_span(method.def_id);
327+
return Some(MethodViolationCode::WhereClauseReferencesSelf(span));
328+
}
329+
291330
None
292331
}
293332

src/librustc/ty/fold.rs

+14
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,20 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
136136
fn has_late_bound_regions(&self) -> bool {
137137
self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND)
138138
}
139+
140+
/// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`.
141+
fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool {
142+
143+
pub struct Visitor<F>(F);
144+
145+
impl<'tcx, F: FnMut(Ty<'tcx>) -> bool> TypeVisitor<'tcx> for Visitor<F> {
146+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
147+
self.0(ty)
148+
}
149+
}
150+
151+
self.visit_with(&mut Visitor(visit))
152+
}
139153
}
140154

141155
/// The TypeFolder trait defines the actual *folding*. There is a

src/librustc_lint/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
292292
reference: "issue TBD",
293293
edition: Some(Edition::Edition2018),
294294
},
295+
FutureIncompatibleInfo {
296+
id: LintId::of(WHERE_CLAUSES_OBJECT_SAFETY),
297+
reference: "issue #51443 <https://github.com/rust-lang/rust/issues/51443>",
298+
edition: None,
299+
},
295300
FutureIncompatibleInfo {
296301
id: LintId::of(DUPLICATE_ASSOCIATED_TYPE_BINDINGS),
297302
reference: "issue #50589 <https://github.com/rust-lang/rust/issues/50589>",

src/test/compile-fail/issue-43431.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#![feature(fn_traits)]
1212

1313
trait CallSingle<A, B> {
14-
fn call(&self, a: A) -> B where Self: Fn(A) -> B;
14+
fn call(&self, a: A) -> B where Self: Sized, Self: Fn(A) -> B;
1515
}
1616

1717
impl<A, B, F: Fn(A) -> B> CallSingle<A, B> for F {

src/test/compile-fail/wf-trait-fn-where-clause.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
struct Bar<T:Eq+?Sized> { value: Box<T> }
1818

1919
trait Foo {
20-
fn bar(&self) where Bar<Self>: Copy;
20+
fn bar(&self) where Self: Sized, Bar<Self>: Copy;
2121
//~^ ERROR E0277
2222
//
2323
// Here, Eq ought to be implemented.

src/test/run-pass/issue-23435.rs

-37
This file was deleted.

src/test/ui/issue-50781.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2018 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+
#![deny(where_clauses_object_safety)]
12+
13+
trait Trait {}
14+
15+
trait X {
16+
fn foo(&self) where Self: Trait; //~ ERROR the trait `X` cannot be made into an object
17+
//~^ WARN this was previously accepted by the compiler but is being phased out
18+
}
19+
20+
impl X for () {
21+
fn foo(&self) {}
22+
}
23+
24+
impl Trait for dyn X {}
25+
26+
pub fn main() {
27+
// Check that this does not segfault.
28+
<X as X>::foo(&());
29+
}

src/test/ui/issue-50781.stderr

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: the trait `X` cannot be made into an object
2+
--> $DIR/issue-50781.rs:16:5
3+
|
4+
LL | fn foo(&self) where Self: Trait; //~ ERROR the trait `X` cannot be made into an object
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/issue-50781.rs:11:9
9+
|
10+
LL | #![deny(where_clauses_object_safety)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
13+
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
14+
= note: method `foo` references the `Self` type in where clauses
15+
16+
error: aborting due to previous error
17+

0 commit comments

Comments
 (0)