Skip to content

Commit

Permalink
Rollup merge of rust-lang#122192 - oli-obk:type_of_opaque_for_const_c…
Browse files Browse the repository at this point in the history
…hecks, r=lcnr

Do not try to reveal hidden types when trying to prove Freeze in the defining scope

fixes rust-lang#99793

this avoids the cycle error by just causing a selection error, which is not fatal. We pessimistically assume that freeze does not hold, which is always a safe assumption.
  • Loading branch information
fmease authored Jun 4, 2024
2 parents a330e49 + ad6e85b commit c43eb8e
Show file tree
Hide file tree
Showing 16 changed files with 211 additions and 68 deletions.
28 changes: 27 additions & 1 deletion compiler/rustc_const_eval/src/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,33 @@ impl Qualif for HasMutInterior {
}

fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
!ty.is_freeze(cx.tcx, cx.param_env)
// Avoid selecting for simple cases, such as builtin types.
if ty.is_trivially_freeze() {
return false;
}

// We do not use `ty.is_freeze` here, because that requires revealing opaque types, which
// requires borrowck, which in turn will invoke mir_const_qualifs again, causing a cycle error.
// Instead we invoke an obligation context manually, and provide the opaque type inference settings
// that allow the trait solver to just error out instead of cycling.
let freeze_def_id = cx.tcx.require_lang_item(LangItem::Freeze, Some(cx.body.span));

let obligation = Obligation::new(
cx.tcx,
ObligationCause::dummy_with_span(cx.body.span),
cx.param_env,
ty::TraitRef::new(cx.tcx, freeze_def_id, [ty::GenericArg::from(ty)]),
);

let infcx = cx
.tcx
.infer_ctxt()
.with_opaque_type_inference(cx.body.source.def_id().expect_local())
.build();
let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
!errors.is_empty()
}

fn in_adt_inherently<'tcx>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ impl<'tcx> Ty<'tcx> {
///
/// Returning true means the type is known to be `Freeze`. Returning
/// `false` means nothing -- could be `Freeze`, might not be.
fn is_trivially_freeze(self) -> bool {
pub fn is_trivially_freeze(self) -> bool {
match self.kind() {
ty::Int(_)
| ty::Uint(_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}

ty::Alias(ty::Opaque, _) => {
ty::Alias(ty::Opaque, alias) => {
if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
// We do not generate an auto impl candidate for `impl Trait`s which already
// reference our auto trait.
Expand All @@ -792,6 +792,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// We do not emit auto trait candidates for opaque types in coherence.
// Doing so can result in weird dependency cycles.
candidates.ambiguous = true;
} else if self.infcx.can_define_opaque_ty(alias.def_id) {
// We do not emit auto trait candidates for opaque types in their defining scope, as
// we need to know the hidden type first, which we can't reliably know within the defining
// scope.
candidates.ambiguous = true;
} else {
candidates.vec.push(AutoImplCandidate)
}
Expand Down
19 changes: 12 additions & 7 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2417,13 +2417,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}

ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
match self.tcx().type_of_opaque(def_id) {
Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
Err(_) => {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
if self.infcx.can_define_opaque_ty(def_id) {
// We cannot possibly resolve this opaque type, because we are currently computing its hidden type.
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
} else {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
match self.tcx().type_of_opaque(def_id) {
Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
Err(_) => {
return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
}
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/const-generics/opaque_types.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ note: ...which requires const checking `main::{constant#0}`...
|
LL | foo::<42>();
| ^^
= note: ...which requires computing whether `Foo` is freeze...
= note: ...which requires evaluating trait selection obligation `Foo: core::marker::Freeze`...
= note: ...which again requires computing type of opaque `Foo::{opaque#0}`, completing the cycle
note: cycle used when computing type of `Foo::{opaque#0}`
--> $DIR/opaque_types.rs:3:12
Expand Down
22 changes: 22 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0283]: type annotations needed
--> $DIR/auto-trait-selection-freeze.rs:19:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ ----- type must be known at this point
| |
| cannot infer type of the type parameter `T` declared on the function `is_trait`
|
= note: cannot satisfy `_: Trait<_>`
note: required by a bound in `is_trait`
--> $DIR/auto-trait-selection-freeze.rs:11:16
|
LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
| ^^^^^^^^ required by this bound in `is_trait`
help: consider specifying the generic arguments
|
LL | if false { is_trait::<T, U>(foo()) } else { Default::default() }
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
26 changes: 26 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection-freeze.old.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0283]: type annotations needed
--> $DIR/auto-trait-selection-freeze.rs:19:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `is_trait`
|
note: multiple `impl`s satisfying `impl Sized: Trait<_>` found
--> $DIR/auto-trait-selection-freeze.rs:16:1
|
LL | impl<T: Freeze> Trait<u32> for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl<T> Trait<i32> for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `is_trait`
--> $DIR/auto-trait-selection-freeze.rs:11:16
|
LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
| ^^^^^^^^ required by this bound in `is_trait`
help: consider specifying the generic arguments
|
LL | if false { is_trait::<_, U>(foo()) } else { Default::default() }
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
23 changes: 23 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection-freeze.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! This test shows how we fail selection in a way that can influence
//! selection in a code path that succeeds.
//@ revisions: next old
//@[next] compile-flags: -Znext-solver

#![feature(freeze)]

use std::marker::Freeze;

fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
Default::default()
}

trait Trait<T> {}
impl<T: Freeze> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn foo() -> impl Sized {
if false { is_trait(foo()) } else { Default::default() }
//~^ ERROR: type annotations needed
}

fn main() {}
22 changes: 22 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0283]: type annotations needed
--> $DIR/auto-trait-selection.rs:15:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ ----- type must be known at this point
| |
| cannot infer type of the type parameter `T` declared on the function `is_trait`
|
= note: cannot satisfy `_: Trait<_>`
note: required by a bound in `is_trait`
--> $DIR/auto-trait-selection.rs:7:16
|
LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
| ^^^^^^^^ required by this bound in `is_trait`
help: consider specifying the generic arguments
|
LL | if false { is_trait::<T, U>(foo()) } else { Default::default() }
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
26 changes: 26 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection.old.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0283]: type annotations needed
--> $DIR/auto-trait-selection.rs:15:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `is_trait`
|
note: multiple `impl`s satisfying `impl Sized: Trait<_>` found
--> $DIR/auto-trait-selection.rs:12:1
|
LL | impl<T: Send> Trait<u32> for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl<T> Trait<i32> for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `is_trait`
--> $DIR/auto-trait-selection.rs:7:16
|
LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
| ^^^^^^^^ required by this bound in `is_trait`
help: consider specifying the generic arguments
|
LL | if false { is_trait::<_, U>(foo()) } else { Default::default() }
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
19 changes: 19 additions & 0 deletions tests/ui/impl-trait/auto-trait-selection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! This test shows how we fail selection in a way that can influence
//! selection in a code path that succeeds.
//@ revisions: next old
//@[next] compile-flags: -Znext-solver

fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
Default::default()
}

trait Trait<T> {}
impl<T: Send> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn foo() -> impl Sized {
if false { is_trait(foo()) } else { Default::default() }
//~^ ERROR: type annotations needed
}

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/impl-trait/rpit/const_check_false_cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! This test caused a cycle error when checking whether the
//! return type is `Freeze` during const checking, even though
//! the information is readily available.
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ check-pass

const fn f() -> impl Eq {
g()
}
const fn g() {}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const fn test() -> impl ~const Fn() {
//~^ ERROR `~const` can only be applied to `#[const_trait]` traits
//~| ERROR `~const` can only be applied to `#[const_trait]` traits
//~| ERROR cycle detected
const move || { //~ ERROR const closures are experimental
let sl: &[u8] = b"foo";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0658]: const closures are experimental
--> $DIR/ice-112822-expected-type-for-param.rs:7:5
--> $DIR/ice-112822-expected-type-for-param.rs:6:5
|
LL | const move || {
| ^^^^^
Expand All @@ -23,46 +23,15 @@ LL | const fn test() -> impl ~const Fn() {
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0277]: can't compare `&u8` with `&u8`
--> $DIR/ice-112822-expected-type-for-param.rs:12:17
--> $DIR/ice-112822-expected-type-for-param.rs:11:17
|
LL | assert_eq!(first, &b'f');
| ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&u8 == &u8`
|
= help: the trait `~const PartialEq<&u8>` is not implemented for `&u8`
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0391]: cycle detected when computing type of opaque `test::{opaque#0}`
--> $DIR/ice-112822-expected-type-for-param.rs:3:20
|
LL | const fn test() -> impl ~const Fn() {
| ^^^^^^^^^^^^^^^^
|
note: ...which requires borrow-checking `test`...
--> $DIR/ice-112822-expected-type-for-param.rs:3:1
|
LL | const fn test() -> impl ~const Fn() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `test`...
--> $DIR/ice-112822-expected-type-for-param.rs:3:1
|
LL | const fn test() -> impl ~const Fn() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires const checking `test`...
--> $DIR/ice-112822-expected-type-for-param.rs:3:1
|
LL | const fn test() -> impl ~const Fn() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing whether `test::{opaque#0}` is freeze...
= note: ...which requires evaluating trait selection obligation `test::{opaque#0}: core::marker::Freeze`...
= note: ...which again requires computing type of opaque `test::{opaque#0}`, completing the cycle
note: cycle used when computing type of `test::{opaque#0}`
--> $DIR/ice-112822-expected-type-for-param.rs:3:20
|
LL | const fn test() -> impl ~const Fn() {
| ^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0277, E0391, E0658.
Some errors have detailed explanations: E0277, E0658.
For more information about an error, try `rustc --explain E0277`.
2 changes: 1 addition & 1 deletion tests/ui/type-alias-impl-trait/in-where-clause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
#![feature(type_alias_impl_trait)]
type Bar = impl Sized;
//~^ ERROR: cycle
//~| ERROR: cycle

fn foo() -> Bar
where
Bar: Send,
//~^ ERROR: type annotations needed
{
[0; 1 + 2]
}
Expand Down
27 changes: 8 additions & 19 deletions tests/ui/type-alias-impl-trait/in-where-clause.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ note: ...which requires computing type of opaque `Bar::{opaque#0}`...
LL | type Bar = impl Sized;
| ^^^^^^^^^^
note: ...which requires type-checking `foo`...
--> $DIR/in-where-clause.rs:9:1
--> $DIR/in-where-clause.rs:8:1
|
LL | / fn foo() -> Bar
LL | | where
Expand All @@ -25,26 +25,15 @@ LL | type Bar = impl Sized;
| ^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error[E0391]: cycle detected when computing type of opaque `Bar::{opaque#0}`
--> $DIR/in-where-clause.rs:5:12
|
LL | type Bar = impl Sized;
| ^^^^^^^^^^
|
note: ...which requires type-checking `foo`...
--> $DIR/in-where-clause.rs:13:9
error[E0283]: type annotations needed: cannot satisfy `Bar: Send`
--> $DIR/in-where-clause.rs:10:10
|
LL | [0; 1 + 2]
| ^^^^^
= note: ...which requires evaluating trait selection obligation `Bar: core::marker::Send`...
= note: ...which again requires computing type of opaque `Bar::{opaque#0}`, completing the cycle
note: cycle used when computing type of `Bar::{opaque#0}`
--> $DIR/in-where-clause.rs:5:12
LL | Bar: Send,
| ^^^^
|
LL | type Bar = impl Sized;
| ^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
= note: cannot satisfy `Bar: Send`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0391`.
Some errors have detailed explanations: E0283, E0391.
For more information about an error, try `rustc --explain E0283`.

0 comments on commit c43eb8e

Please sign in to comment.