Skip to content

Commit 35f92ed

Browse files
authored
Rollup merge of rust-lang#102568 - compiler-errors:lint-unsatisfied-opaques, r=oli-obk
Lint against nested opaque types that don't satisfy associated type bounds See the test failures for examples of places where this lint would fire. r? `@oli-obk`
2 parents 32dde23 + 7a88540 commit 35f92ed

16 files changed

+274
-1
lines changed

compiler/rustc_error_messages/locales/en-US/lint.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,7 @@ lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
433433
lint_check_name_warning = {$msg}
434434
435435
lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}
436+
437+
lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its associated type bounds
438+
.specifically = this associated type bound is unsatisfied for `{$proj_ty}`
439+
.suggestion = add this bound

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ mod non_ascii_idents;
6262
mod non_fmt_panic;
6363
mod nonstandard_style;
6464
mod noop_method_call;
65+
mod opaque_hidden_inferred_bound;
6566
mod pass_by_value;
6667
mod passes;
6768
mod redundant_semicolon;
@@ -93,6 +94,7 @@ use non_ascii_idents::*;
9394
use non_fmt_panic::NonPanicFmt;
9495
use nonstandard_style::*;
9596
use noop_method_call::*;
97+
use opaque_hidden_inferred_bound::*;
9698
use pass_by_value::*;
9799
use redundant_semicolon::*;
98100
use traits::*;
@@ -223,6 +225,7 @@ macro_rules! late_lint_mod_passes {
223225
EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
224226
InvalidAtomicOrdering: InvalidAtomicOrdering,
225227
NamedAsmLabels: NamedAsmLabels,
228+
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
226229
]
227230
);
228231
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use rustc_hir as hir;
2+
use rustc_infer::infer::TyCtxtInferExt;
3+
use rustc_macros::LintDiagnostic;
4+
use rustc_middle::ty::{self, fold::BottomUpFolder, Ty, TypeFoldable};
5+
use rustc_span::Span;
6+
use rustc_trait_selection::traits;
7+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
8+
9+
use crate::{LateContext, LateLintPass, LintContext};
10+
11+
declare_lint! {
12+
/// The `opaque_hidden_inferred_bound` lint detects cases in which nested
13+
/// `impl Trait` in associated type bounds are not written generally enough
14+
/// to satisfy the bounds of the associated type.
15+
///
16+
/// ### Explanation
17+
///
18+
/// This functionality was removed in #97346, but then rolled back in #99860
19+
/// because it caused regressions.
20+
///
21+
/// We plan on reintroducing this as a hard error, but in the mean time,
22+
/// this lint serves to warn and suggest fixes for any use-cases which rely
23+
/// on this behavior.
24+
///
25+
/// ### Example
26+
///
27+
/// ```
28+
/// trait Trait {
29+
/// type Assoc: Send;
30+
/// }
31+
///
32+
/// struct Struct;
33+
///
34+
/// impl Trait for Struct {
35+
/// type Assoc = i32;
36+
/// }
37+
///
38+
/// fn test() -> impl Trait<Assoc = impl Sized> {
39+
/// Struct
40+
/// }
41+
/// ```
42+
///
43+
/// {{produces}}
44+
///
45+
/// In this example, `test` declares that the associated type `Assoc` for
46+
/// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound
47+
/// on the associated type.
48+
///
49+
/// Although the hidden type, `i32` does satisfy this bound, we do not
50+
/// consider the return type to be well-formed with this lint. It can be
51+
/// fixed by changing `impl Sized` into `impl Sized + Send`.
52+
pub OPAQUE_HIDDEN_INFERRED_BOUND,
53+
Warn,
54+
"detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
55+
}
56+
57+
declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
58+
59+
impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
60+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
61+
let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
62+
let def_id = item.def_id.def_id.to_def_id();
63+
cx.tcx.infer_ctxt().enter(|ref infcx| {
64+
// For every projection predicate in the opaque type's explicit bounds,
65+
// check that the type that we're assigning actually satisfies the bounds
66+
// of the associated type.
67+
for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
68+
// Liberate bound regions in the predicate since we
69+
// don't actually care about lifetimes in this check.
70+
let predicate = cx.tcx.liberate_late_bound_regions(
71+
def_id,
72+
pred.kind(),
73+
);
74+
let ty::PredicateKind::Projection(proj) = predicate else {
75+
continue;
76+
};
77+
// Only check types, since those are the only things that may
78+
// have opaques in them anyways.
79+
let Some(proj_term) = proj.term.ty() else { continue };
80+
81+
let proj_ty =
82+
cx
83+
.tcx
84+
.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
85+
// For every instance of the projection type in the bounds,
86+
// replace them with the term we're assigning to the associated
87+
// type in our opaque type.
88+
let proj_replacer = &mut BottomUpFolder {
89+
tcx: cx.tcx,
90+
ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
91+
lt_op: |lt| lt,
92+
ct_op: |ct| ct,
93+
};
94+
// For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
95+
// e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
96+
// with `impl Send: OtherTrait`.
97+
for assoc_pred_and_span in cx
98+
.tcx
99+
.bound_explicit_item_bounds(proj.projection_ty.item_def_id)
100+
.transpose_iter()
101+
{
102+
let assoc_pred_span = assoc_pred_and_span.0.1;
103+
let assoc_pred = assoc_pred_and_span
104+
.map_bound(|(pred, _)| *pred)
105+
.subst(cx.tcx, &proj.projection_ty.substs)
106+
.fold_with(proj_replacer);
107+
let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
108+
continue;
109+
};
110+
// If that predicate doesn't hold modulo regions (but passed during type-check),
111+
// then we must've taken advantage of the hack in `project_and_unify_types` where
112+
// we replace opaques with inference vars. Emit a warning!
113+
if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
114+
traits::ObligationCause::dummy(),
115+
cx.param_env,
116+
assoc_pred,
117+
)) {
118+
// If it's a trait bound and an opaque that doesn't satisfy it,
119+
// then we can emit a suggestion to add the bound.
120+
let (suggestion, suggest_span) =
121+
match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
122+
(ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => (
123+
format!(" + {}", trait_pred.print_modifiers_and_trait_path()),
124+
Some(cx.tcx.def_span(def_id).shrink_to_hi()),
125+
),
126+
_ => (String::new(), None),
127+
};
128+
cx.emit_spanned_lint(
129+
OPAQUE_HIDDEN_INFERRED_BOUND,
130+
pred_span,
131+
OpaqueHiddenInferredBoundLint {
132+
ty: cx.tcx.mk_opaque(def_id, ty::InternalSubsts::identity_for_item(cx.tcx, def_id)),
133+
proj_ty: proj_term,
134+
assoc_pred_span,
135+
suggestion,
136+
suggest_span,
137+
},
138+
);
139+
}
140+
}
141+
}
142+
});
143+
}
144+
}
145+
146+
#[derive(LintDiagnostic)]
147+
#[diag(lint::opaque_hidden_inferred_bound)]
148+
struct OpaqueHiddenInferredBoundLint<'tcx> {
149+
ty: Ty<'tcx>,
150+
proj_ty: Ty<'tcx>,
151+
#[label(lint::specifically)]
152+
assoc_pred_span: Span,
153+
#[suggestion_verbose(applicability = "machine-applicable", code = "{suggestion}")]
154+
suggest_span: Option<Span>,
155+
suggestion: String,
156+
}

compiler/rustc_middle/src/lint.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,9 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
446446
match expn_data.kind {
447447
ExpnKind::Inlined
448448
| ExpnKind::Root
449-
| ExpnKind::Desugaring(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) => false,
449+
| ExpnKind::Desugaring(
450+
DesugaringKind::ForLoop | DesugaringKind::WhileLoop | DesugaringKind::OpaqueTy,
451+
) => false,
450452
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
451453
ExpnKind::Macro(MacroKind::Bang, _) => {
452454
// Dummy span for the `def_site` means it's an external macro.

src/test/ui/impl-trait/nested-return-type2-tait.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type Sendable = impl Send;
2626
// var to make it uphold the `: Duh` bound on `Trait::Assoc`. The opaque
2727
// type does not implement `Duh`, but if its hidden type does.
2828
fn foo() -> impl Trait<Assoc = Sendable> {
29+
//~^ WARN opaque type `impl Trait<Assoc = Sendable>` does not satisfy its associated type bounds
2930
|| 42
3031
}
3132

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `impl Trait<Assoc = Sendable>` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type2-tait.rs:28:24
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `Sendable`
6+
...
7+
LL | fn foo() -> impl Trait<Assoc = Sendable> {
8+
| ^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | type Sendable = impl Send + Duh;
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

src/test/ui/impl-trait/nested-return-type2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<R: Duh, F: FnMut() -> R> Trait for F {
2323
// Lazy TAIT would error out, but we inserted a hack to make it work again,
2424
// keeping backwards compatibility.
2525
fn foo() -> impl Trait<Assoc = impl Send> {
26+
//~^ WARN opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
2627
|| 42
2728
}
2829

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type2.rs:25:24
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `impl Send`
6+
...
7+
LL | fn foo() -> impl Trait<Assoc = impl Send> {
8+
| ^^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | fn foo() -> impl Trait<Assoc = impl Send + Duh> {
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

src/test/ui/impl-trait/nested-return-type3-tait.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ impl<F: Duh> Trait for F {
1717
type Sendable = impl Send;
1818

1919
fn foo() -> impl Trait<Assoc = Sendable> {
20+
//~^ WARN opaque type `impl Trait<Assoc = Sendable>` does not satisfy its associated type bounds
2021
42
2122
}
2223

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `impl Trait<Assoc = Sendable>` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type3-tait.rs:19:24
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `Sendable`
6+
...
7+
LL | fn foo() -> impl Trait<Assoc = Sendable> {
8+
| ^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | type Sendable = impl Send + Duh;
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

src/test/ui/impl-trait/nested-return-type3-tait2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ impl<F: Duh> Trait for F {
1616

1717
type Sendable = impl Send;
1818
type Traitable = impl Trait<Assoc = Sendable>;
19+
//~^ WARN opaque type `Traitable` does not satisfy its associated type bounds
1920

2021
fn foo() -> Traitable {
2122
42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `Traitable` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type3-tait2.rs:18:29
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `Sendable`
6+
...
7+
LL | type Traitable = impl Trait<Assoc = Sendable>;
8+
| ^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | type Sendable = impl Send + Duh;
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

src/test/ui/impl-trait/nested-return-type3-tait3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ impl<F: Duh> Trait for F {
1515
}
1616

1717
type Traitable = impl Trait<Assoc = impl Send>;
18+
//~^ WARN opaque type `Traitable` does not satisfy its associated type bounds
1819

1920
fn foo() -> Traitable {
2021
42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `Traitable` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type3-tait3.rs:17:29
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `impl Send`
6+
...
7+
LL | type Traitable = impl Trait<Assoc = impl Send>;
8+
| ^^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | type Traitable = impl Trait<Assoc = impl Send + Duh>;
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

src/test/ui/impl-trait/nested-return-type3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ impl<F: Duh> Trait for F {
1313
}
1414

1515
fn foo() -> impl Trait<Assoc = impl Send> {
16+
//~^ WARN opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
1617
42
1718
}
1819

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
2+
--> $DIR/nested-return-type3.rs:15:24
3+
|
4+
LL | type Assoc: Duh;
5+
| --- this associated type bound is unsatisfied for `impl Send`
6+
...
7+
LL | fn foo() -> impl Trait<Assoc = impl Send> {
8+
| ^^^^^^^^^^^^^^^^^
9+
|
10+
= note: `#[warn(opaque_hidden_inferred_bound)]` on by default
11+
help: add this bound
12+
|
13+
LL | fn foo() -> impl Trait<Assoc = impl Send + Duh> {
14+
| +++++
15+
16+
warning: 1 warning emitted
17+

0 commit comments

Comments
 (0)