Avoid leaking opaque hidden types via auto trait candidates#159589
Avoid leaking opaque hidden types via auto trait candidates#159589Shourya742 wants to merge 3 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.
This thing,
The hack, if bounds are present return ambiguous, (this is what is done in this PR)
let param_env_may_leak_hidden_ty = !goal.param_env.caller_bounds().is_empty();
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 => {
ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS)
}
Err(err) => Err(err)
}Without hack, just propagate error (but this cause fudge to normalize and make obligation part of diag)
return match candidate {
Ok(candidate) if has_only_region_constraints(candidate.result) => Ok(candidate),
Ok(_) => ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS),
Err(err) => Err(err)
}| CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), | ||
| source, | ||
| goal, | ||
| structural_traits::instantiate_constituent_tys_for_auto_trait, |
There was a problem hiding this comment.
please remove the ty::Opaque branch from instantiate_constituent_tys_for_auto_trait as it should now be unreachable 🤔
| // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs. | ||
| if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = | ||
| let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); | ||
| if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = |
There was a problem hiding this comment.
hmm, please move this entire branch into a sub function and change this function to an if else
…onsider_auto_trait_candidate
closes: #134578