Avoid leaking opaque hidden types via auto trait candidates - #159589
Avoid leaking opaque hidden types via auto trait candidates#159589bit-aloo wants to merge 6 commits into
Conversation
|
r? @nnethercote rustbot has assigned @nnethercote. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
r? @lcnr |
| return match candidate { | ||
| Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate), | ||
| Ok(_) => ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS), | ||
| Err(NoSolutionOrRerunNonErased::NoSolution(_)) if param_env_may_leak_hidden_ty => { |
There was a problem hiding this comment.
Turns out NoSolution can leak hidden types too, since the failed proof can go through caller bounds and end up comparing the hidden type with something visible to the caller.
For now I went conservative, if the hidden-type proof fails and there are caller bounds around, I return ambiguity. That’s probably way broader than necessary, since totally unrelated bounds would get caught by this too. Curious if on what are your thoughts on this.
There was a problem hiding this comment.
I don't get what you mean here 🤔 can you provide an example where NoSolution causes problems?
There was a problem hiding this comment.
In both the cases, the fudge result is different depending on whether the hidden type proof returns ambiguity or propagates NoSolution.
This is without the hack, where we return the error as is.
2ms DEBUG rustc_infer::infer::snapshot::fudge return=Ok((ObligationCause { span: /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:25:18: 25:45 (#0), body_def_id: DefId(0:11 ~ opaque_hidden_ty_inference[daf7]::leak), code: ImplDerived(ImplDerivedCause { derived: DerivedCause { parent_trait_pred: Binder { value: TraitPredicate(<dep::WaddupGamers<T, {closure@dep::define<T>::{closure#0}}> as std::marker::Unpin>, polarity:Positive), bound_vars: [] }, parent_code: BuiltinDerived(DerivedCause { parent_trait_pred: Binder { value: TraitPredicate(<impl Sized as std::marker::Unpin>, polarity:Positive), bound_vars: [] }, parent_code: WhereClauseInExpr(DefId(0:5 ~ opaque_hidden_ty_inference[daf7]::require_auto), /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:12:20: 12:25 (#0), HirId(DefId(0:11 ~ opaque_hidden_ty_inference[daf7]::leak).4), 1) }) }, impl_or_alias_def_id: DefId(20:9 ~ opaque_auto_trait_leakage[0084]::{impl#0}), impl_def_predicate_index: Some(0), span: /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs:3:14: 3:23 (#0) }) }, Obligation(predicate=Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(20:13 ~ opaque_auto_trait_leakage[0084]::Leak::Assoc) }, args: [T/#0], .. }, Term::Ty(Closure(DefId(20:20 ~ opaque_auto_trait_leakage[0084]::define::{closure#0}), [T/#0, i8, Binder { value: extern "RustCall" fn(()), bound_vars: [] }, ()]))), bound_vars: [] }, depth=2)))The important part is that the diagnostic cause is now ImplDerived, with the parent obligation:
<dep::WaddupGamers<T, {closure@dep::define<T>::{closure#0}}> as std::marker::Unpin>and the derived obligation:
<T as Leak>::Assoc = Closure(... define::{closure#0} ...)So the hidden closure becomes part of the diagnostic obligation.
With the hack, where we turn this hidden type NoSolution into ambiguity, the fudge result stays at the original require_auto obligation:
1ms DEBUG rustc_infer::infer::snapshot::fudge return=Ok((ObligationCause { span: /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:25:18: 25:45 (#0), body_def_id: DefId(0:11 ~ opaque_hidden_ty_inference[daf7]::leak), code: WhereClauseInExpr(DefId(0:5 ~ opaque_hidden_ty_inference[daf7]::require_auto), /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:12:20: 12:25 (#0), HirId(DefId(0:11 ~ opaque_hidden_ty_inference[daf7]::leak).4), 1) }, Obligation(predicate=Binder { value: TraitPredicate(<impl Sized as std::marker::Unpin>, polarity:Positive), bound_vars: [] }, depth=0)))Here the captured diagnostic obligation is only:
<impl Sized as std::marker::Unpin>With ambiguity we do not capture the hidden WaddupGamers<T, closure> impl derived obligation as the diag cause. Without the hack, the hard NoSolution lets diag's select that hidden impl derived obligation, which is how the closure leaks into the diag.
There was a problem hiding this comment.
Not sure this is the right fix, but hard failure here lets diagnostics pick up details the caller shouldn’t see.
There was a problem hiding this comment.
what is the code snippet whose behavior changes?
There was a problem hiding this comment.
What I want to know is: if you don't do anything for Err(NoSolution) here, what is an example of a Rust code snippet whose compilation result is undesirable?
Not sure this is the right fix, but hard failure here lets diagnostics pick up details the caller shouldn’t see.
I.e. when is this actually the case
There was a problem hiding this comment.
Ah!!! so the test itself fails, if we don't do anything for Err(NoSolution). And this is the error log.
stderr -------------------------------
error[E0271]: type mismatch resolving `<T as Leak>::Assoc == {closure@define<T>::{closure#0}}`
--> /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:25:31
|
LL | let opaque = require_auto(define::<T>());
| ------------ ^^^^^^^^^^^^^ expected closure, found `()`
| |
| required by a bound introduced by this call
|
::: /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs:14:29
|
LL | WaddupGamers(None::<T>, || ())
| -- the expected closure
|
= note: expected closure `{closure@dep::define<T>::{closure#0}}`
found unit type `()`
= note: required for `WaddupGamers<T, {closure@dep::define<T>::{closure#0}}>` to implement `Unpin`
note: required because it appears within the type `impl Sized`
--> /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/auxiliary/opaque-auto-trait-leakage.rs:13:23
|
LL | pub fn define<T>() -> impl Sized {
| ^^^^^^^^^^
note: required by a bound in `require_auto`
--> /home/gh-Shourya742/rust/tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs:12:20
|
LL | fn require_auto<T: Unpin>(x: T) -> T {
| ^^^^^ required by this bound in `require_auto`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.
------------------------------------------
---- [ui] tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rs stdout end ----
failures:
[ui] tests/ui/impl-trait/auto-trait-leakage/opaque-hidden-ty-inference.rsThere was a problem hiding this comment.
We have the test added in PR, same as the one in issue.
There was a problem hiding this comment.
ah, so the problem that errors leak through to diagnostics 🤔
This does result in ambiguity errors while I do think we should emit a proper hard errors here 🤔 this breaks existing supported code.
We should be able to have some Struct<T, U> with impl<T: Send> Struct<T, u32>
and impl<T> Struct<T, i32> where both impls have an inherent method of the same name. If you now call this inherent method with a receiver of type Struct<some_opaque, ?x> where the opaque type does not implement Send we should select the i32 method.
use std::rc::Rc;
struct Struct<T, U>(T, U);
impl<T: Send> Struct<T, u32> {
fn method(&self) {}
}
impl<T> Struct<T, i32> {
fn method(&self) {}
}
fn make_opaque() -> impl Sized {
Rc::new(())
}
fn main() {
Struct(make_opaque(), Default::default()).method();
}I think instead we should change the inspect diagnostics visitor to not look into "opaque type auto trait candidates" 🤔 😁
There was a problem hiding this comment.
Was able to limit the diagnostic leak here: c54dd33
This comment has been minimized.
This comment has been minimized.
2e546e0 to
7c319d5
Compare
7c319d5 to
c54dd33
Compare
|
Some changes occurred to the core trait solver cc @rust-lang/initiative-trait-system-refactor |
| } | ||
| } | ||
|
|
||
| pub(in crate::solve) fn consider_auto_trait_candidate_for_opaque_ty<D, I>( |
There was a problem hiding this comment.
please move this function into trait_goals.rs 🤔
it is only used from there and it doesn't feel "purely structural" enough to belong in this file
| return false; | ||
| } | ||
|
|
||
| let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = |
There was a problem hiding this comment.
| let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = | |
| let ty::Alias(ty::Rigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }) = |
| return false; | ||
| }; | ||
|
|
||
| !matches!(tcx.opaque_ty_origin(*def_id), hir::OpaqueTyOrigin::AsyncFn { .. }) |
There was a problem hiding this comment.
There was a problem hiding this comment.
If we remove this we have two failing test:
[ui] tests/ui/traits/error-reporting/leaking-vars-in-cause-code-1.rs
[ui] tests/ui/traits/next-solver/auto-with-drop_tracking_mir.rs#fail
For auto-with-drop_tracking_mir.rs, we lose the cause and .await location:
@@ -1,29 +1,12 @@
-error[E0277]: `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` cannot be shared between threads safely
+error[E0277]: `impl Future<Output = ()>` cannot be sent between threads safely
--> $DIR/leaking-vars-in-cause-code-1.rs:32:17
|
LL | assert_send(cursed_fut());
- | ----------- ^^^^^^^^^^^^ `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` cannot be shared between threads safely
+ | ----------- ^^^^^^^^^^^^ `impl Future<Output = ()>` cannot be sent between threads safely
| |
| required by a bound introduced by this call
|
- = help: the trait `Sync` is not implemented for `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>`
- = note: required for `&dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` to implement `Send`
-note: required because it's used within this `async` fn body
- --> $DIR/leaking-vars-in-cause-code-1.rs:19:53
- |
-LL | async fn wrap_call<P: AsyncFn + ?Sized>(filter: &P) {
- | _____________________________________________________^
-LL | | filter.call().await;
-LL | | }
- | |_^
-note: required because it's used within this `async` fn body
- --> $DIR/leaking-vars-in-cause-code-1.rs:27:23
- |
-LL | async fn cursed_fut() {
- | _______________________^
-LL | | wrap_call(get_boxed_fn().as_ref()).await;
-LL | | }
- | |_^
+ = help: the trait `Send` is not implemented for `impl Future<Output = ()>`
note: required by a bound in `assert_send`
--> $DIR/leaking-vars-in-cause-code-1.rs:36:19
|For leaking-vars-in-cause-code-1.rs, we also lose the nested async-function context:
@@ -1,21 +1,12 @@
-error: future cannot be sent between threads safely
+error[E0277]: `impl Future<Output = ()>` cannot be sent between threads safely
--> $DIR/auto-with-drop_tracking_mir.rs:25:13
|
LL | is_send(foo());
- | ^^^^^ future returned by `foo` is not `Send`
+ | ------- ^^^^^ `impl Future<Output = ()>` cannot be sent between threads safely
+ | |
+ | required by a bound introduced by this call
|
-help: the trait `Sync` is not implemented for `NotSync`
- --> $DIR/auto-with-drop_tracking_mir.rs:8:1
- |
-LL | struct NotSync;
- | ^^^^^^^^^^^^^^
-note: future is not `Send` as this value is used across an await
- --> $DIR/auto-with-drop_tracking_mir.rs:16:11
- |
-LL | let x = &NotSync;
- | - has type `&NotSync` which is not `Send`
-LL | bar().await;
- | ^^^^^ await occurs here, with `x` maybe used later
+ = help: the trait `Send` is not implemented for `impl Future<Output = ()>`
note: required by a bound in `is_send`
--> $DIR/auto-with-drop_tracking_mir.rs:24:24
|
@@ -24,3 +15,4 @@
error: aborting due to 1 previous error
+For more information about this error, try `rustc --explain E0277`.It seems like we should continue diagnostic traversal for AsyncFn; otherwise, the resulting diagnostic loses most of its useful context. So I’m not sure we should remove this exception. 🤔 Wanna vibe check on what you think?
| //@ ignore-compare-mode-next-solver | ||
| //@ compile-flags: -Znext-solver | ||
| //@ aux-build:opaque-auto-trait-leakage.rs | ||
|
|
There was a problem hiding this comment.
comment explaining this test please, the relevant issue and a short explanation of what it does
…onsider_auto_trait_candidate
…aque_ty in trait goaks
c54dd33 to
1557821
Compare
View all comments
closes: #134578