Skip to content

Commit ccfc38f

Browse files
authored
Auto merge of #37167 - nikomatsakis:jroesch-issue-18937, r=pnkfelix
detect extra region requirements in impls The current "compare method" check fails to check for the "region obligations" that accrue in the fulfillment context. This branch switches that code to create a `FnCtxt` so that it can invoke the regionck code. Previous crater runs (I haven't done one with the latest tip) have found some small number of affected crates, so I went ahead and introduced a warning cycle. I will kick off a crater run with this branch shortly. This is a [breaking-change] because previously unsound code was accepted. The crater runs also revealed some cases where legitimate code was no longer type-checking, so the branch contains one additional (but orthogonal) change. It improves the elaborator so that we elaborate region requirements more thoroughly. In particular, if we know that `&'a T: 'b`, we now deduce that `T: 'b` and `'a: 'b`. I invested a certain amount of effort in getting a good error message. The error message looks like this: ``` error[E0276]: impl has stricter requirements than trait --> traits-elaborate-projection-region.rs:33:5 | 21 | fn foo() where T: 'a; | --------------------- definition of `foo` from trait ... 33 | fn foo() where U: 'a { } | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a` | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #18937 <#18937> note: lint level defined here --> traits-elaborate-projection-region.rs:12:9 | 12 | #![deny(extra_requirement_in_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ ``` Obviously the warning only prints if this is a _new_ error (that resulted from the bugfix). But all existing errors that fit this description are updated to follow the general template. In order to get the lint to preserve the span-labels and the error code, I separate out the core `Diagnostic` type (which encapsulates the error code, message, span, and children) from the `DiagnosticBuilder` (which layers on a `Handler` that can be used to report errors). I also extended `add_lint` with an alternative `add_lint_diagnostic` that takes in a full diagnostic (cc @jonathandturner for those changes). This doesn't feel ideal but feels like it's moving in the right direction =). r? @pnkfelix cc @arielb1 Fixes #18937
2 parents d2bc30b + 4501e5a commit ccfc38f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1713
-785
lines changed

src/librustc/infer/error_reporting.rs

+73-22
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
245245
debug!("report_region_errors: {} errors after preprocessing", errors.len());
246246

247247
for error in errors {
248+
debug!("report_region_errors: error = {:?}", error);
248249
match error.clone() {
249250
ConcreteFailure(origin, sub, sup) => {
250251
self.report_concrete_failure(origin, sub, sup).emit();
@@ -299,44 +300,64 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
299300
let mut bound_failures = Vec::new();
300301

301302
for error in errors {
303+
// Check whether we can process this error into some other
304+
// form; if not, fall through.
302305
match *error {
303306
ConcreteFailure(ref origin, sub, sup) => {
304307
debug!("processing ConcreteFailure");
305-
match free_regions_from_same_fn(self.tcx, sub, sup) {
306-
Some(ref same_frs) => {
307-
origins.push(
308-
ProcessedErrorOrigin::ConcreteFailure(
309-
origin.clone(),
310-
sub,
311-
sup));
312-
append_to_same_regions(&mut same_regions, same_frs);
313-
}
314-
_ => {
315-
other_errors.push(error.clone());
316-
}
308+
if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin {
309+
// When comparing an impl method against a
310+
// trait method, it is not helpful to suggest
311+
// changes to the impl method. This is
312+
// because the impl method signature is being
313+
// checked using the trait's environment, so
314+
// usually the changes we suggest would
315+
// actually have to be applied to the *trait*
316+
// method (and it's not clear that the trait
317+
// method is even under the user's control).
318+
} else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
319+
origins.push(
320+
ProcessedErrorOrigin::ConcreteFailure(
321+
origin.clone(),
322+
sub,
323+
sup));
324+
append_to_same_regions(&mut same_regions, &same_frs);
325+
continue;
317326
}
318327
}
319-
SubSupConflict(ref var_origin, _, sub_r, _, sup_r) => {
320-
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r);
321-
match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
322-
Some(ref same_frs) => {
323-
origins.push(
324-
ProcessedErrorOrigin::VariableFailure(
325-
var_origin.clone()));
326-
append_to_same_regions(&mut same_regions, same_frs);
328+
SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => {
329+
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup);
330+
match (sub_origin, sup_origin) {
331+
(&SubregionOrigin::CompareImplMethodObligation { .. }, _) => {
332+
// As above, when comparing an impl method
333+
// against a trait method, it is not helpful
334+
// to suggest changes to the impl method.
327335
}
328-
None => {
329-
other_errors.push(error.clone());
336+
(_, &SubregionOrigin::CompareImplMethodObligation { .. }) => {
337+
// See above.
338+
}
339+
_ => {
340+
if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
341+
origins.push(
342+
ProcessedErrorOrigin::VariableFailure(
343+
var_origin.clone()));
344+
append_to_same_regions(&mut same_regions, &same_frs);
345+
continue;
346+
}
330347
}
331348
}
332349
}
333350
GenericBoundFailure(ref origin, ref kind, region) => {
334351
bound_failures.push((origin.clone(), kind.clone(), region));
352+
continue;
335353
}
336354
ProcessedErrors(..) => {
337355
bug!("should not encounter a `ProcessedErrors` yet: {:?}", error)
338356
}
339357
}
358+
359+
// No changes to this error.
360+
other_errors.push(error.clone());
340361
}
341362

342363
// ok, let's pull together the errors, sorted in an order that
@@ -630,6 +651,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
630651
format!("the associated type `{}`", p),
631652
};
632653

654+
if let SubregionOrigin::CompareImplMethodObligation {
655+
span, item_name, impl_item_def_id, trait_item_def_id, lint_id
656+
} = origin {
657+
self.report_extra_impl_obligation(span,
658+
item_name,
659+
impl_item_def_id,
660+
trait_item_def_id,
661+
&format!("`{}: {}`", bound_kind, sub),
662+
lint_id)
663+
.emit();
664+
return;
665+
}
666+
633667
let mut err = match *sub {
634668
ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
635669
// Does the required lifetime have a nice name we can print?
@@ -947,6 +981,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
947981
"");
948982
err
949983
}
984+
infer::CompareImplMethodObligation { span,
985+
item_name,
986+
impl_item_def_id,
987+
trait_item_def_id,
988+
lint_id } => {
989+
self.report_extra_impl_obligation(span,
990+
item_name,
991+
impl_item_def_id,
992+
trait_item_def_id,
993+
&format!("`{}: {}`", sup, sub),
994+
lint_id)
995+
}
950996
}
951997
}
952998

@@ -1792,6 +1838,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
17921838
"...so that references are valid when the destructor \
17931839
runs");
17941840
}
1841+
infer::CompareImplMethodObligation { span, .. } => {
1842+
err.span_note(
1843+
span,
1844+
"...so that the definition in impl matches the definition from the trait");
1845+
}
17951846
}
17961847
}
17971848
}

src/librustc/infer/mod.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@ pub enum SubregionOrigin<'tcx> {
355355

356356
// Region constraint arriving from destructor safety
357357
SafeDestructor(Span),
358+
359+
// Comparing the signature and requirements of an impl method against
360+
// the containing trait.
361+
CompareImplMethodObligation {
362+
span: Span,
363+
item_name: ast::Name,
364+
impl_item_def_id: DefId,
365+
trait_item_def_id: DefId,
366+
367+
// this is `Some(_)` if this error arises from the bug fix for
368+
// #18937. This is a temporary measure.
369+
lint_id: Option<ast::NodeId>,
370+
},
358371
}
359372

360373
/// Places that type/region parameters can appear.
@@ -1147,16 +1160,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
11471160
}
11481161

11491162
pub fn region_outlives_predicate(&self,
1150-
span: Span,
1163+
cause: &traits::ObligationCause<'tcx>,
11511164
predicate: &ty::PolyRegionOutlivesPredicate<'tcx>)
11521165
-> UnitResult<'tcx>
11531166
{
11541167
self.commit_if_ok(|snapshot| {
11551168
let (ty::OutlivesPredicate(r_a, r_b), skol_map) =
11561169
self.skolemize_late_bound_regions(predicate, snapshot);
1157-
let origin = RelateRegionParamBound(span);
1170+
let origin =
1171+
SubregionOrigin::from_obligation_cause(cause,
1172+
|| RelateRegionParamBound(cause.span));
11581173
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
1159-
self.leak_check(false, span, &skol_map, snapshot)?;
1174+
self.leak_check(false, cause.span, &skol_map, snapshot)?;
11601175
Ok(self.pop_skolemized(skol_map, snapshot))
11611176
})
11621177
}
@@ -1786,6 +1801,32 @@ impl<'tcx> SubregionOrigin<'tcx> {
17861801
AddrOf(a) => a,
17871802
AutoBorrow(a) => a,
17881803
SafeDestructor(a) => a,
1804+
CompareImplMethodObligation { span, .. } => span,
1805+
}
1806+
}
1807+
1808+
pub fn from_obligation_cause<F>(cause: &traits::ObligationCause<'tcx>,
1809+
default: F)
1810+
-> Self
1811+
where F: FnOnce() -> Self
1812+
{
1813+
match cause.code {
1814+
traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) =>
1815+
SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span),
1816+
1817+
traits::ObligationCauseCode::CompareImplMethodObligation { item_name,
1818+
impl_item_def_id,
1819+
trait_item_def_id,
1820+
lint_id } =>
1821+
SubregionOrigin::CompareImplMethodObligation {
1822+
span: cause.span,
1823+
item_name: item_name,
1824+
impl_item_def_id: impl_item_def_id,
1825+
trait_item_def_id: trait_item_def_id,
1826+
lint_id: lint_id,
1827+
},
1828+
1829+
_ => default(),
17891830
}
17901831
}
17911832
}

src/librustc/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ declare_lint! {
198198
"patterns in functions without body were erroneously allowed"
199199
}
200200

201+
declare_lint! {
202+
pub EXTRA_REQUIREMENT_IN_IMPL,
203+
Warn,
204+
"detects extra requirements in impls that were erroneously allowed"
205+
}
206+
201207
/// Does nothing as a lint pass, but registers some `Lint`s
202208
/// which are used by other parts of the compiler.
203209
#[derive(Copy, Clone)]
@@ -235,7 +241,8 @@ impl LintPass for HardwiredLints {
235241
HR_LIFETIME_IN_ASSOC_TYPE,
236242
LIFETIME_UNDERSCORE,
237243
SAFE_EXTERN_STATICS,
238-
PATTERNS_IN_FNS_WITHOUT_BODY
244+
PATTERNS_IN_FNS_WITHOUT_BODY,
245+
EXTRA_REQUIREMENT_IN_IMPL
239246
)
240247
}
241248
}

0 commit comments

Comments
 (0)