diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 5949444e599a0..8bd3a8907d3cf 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -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>( diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 52a0e72e17e28..0843f21128f9b 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -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(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 4d3aa067c6c53..b4793ce004cd4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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. @@ -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) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2489b8916d181..b5b8fb4e8cdc7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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)); + } } } } diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr index f03bca69a8bbf..c98e89121f6e4 100644 --- a/tests/ui/const-generics/opaque_types.stderr +++ b/tests/ui/const-generics/opaque_types.stderr @@ -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 diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr new file mode 100644 index 0000000000000..5caf0eb2fd4e3 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr @@ -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, U: Default>(_: T) -> U { + | ^^^^^^^^ required by this bound in `is_trait` +help: consider specifying the generic arguments + | +LL | if false { is_trait::(foo()) } else { Default::default() } + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.old.stderr b/tests/ui/impl-trait/auto-trait-selection-freeze.old.stderr new file mode 100644 index 0000000000000..b4d2229d408d2 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection-freeze.old.stderr @@ -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 Trait for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `is_trait` + --> $DIR/auto-trait-selection-freeze.rs:11:16 + | +LL | fn is_trait, 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`. diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.rs b/tests/ui/impl-trait/auto-trait-selection-freeze.rs new file mode 100644 index 0000000000000..7306a1c41f746 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection-freeze.rs @@ -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, U: Default>(_: T) -> U { + Default::default() +} + +trait Trait {} +impl Trait for T {} +impl Trait for T {} +fn foo() -> impl Sized { + if false { is_trait(foo()) } else { Default::default() } + //~^ ERROR: type annotations needed +} + +fn main() {} diff --git a/tests/ui/impl-trait/auto-trait-selection.next.stderr b/tests/ui/impl-trait/auto-trait-selection.next.stderr new file mode 100644 index 0000000000000..d34fdcc44967f --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection.next.stderr @@ -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, U: Default>(_: T) -> U { + | ^^^^^^^^ required by this bound in `is_trait` +help: consider specifying the generic arguments + | +LL | if false { is_trait::(foo()) } else { Default::default() } + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/impl-trait/auto-trait-selection.old.stderr b/tests/ui/impl-trait/auto-trait-selection.old.stderr new file mode 100644 index 0000000000000..1b5fd95fdf903 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection.old.stderr @@ -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 Trait for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `is_trait` + --> $DIR/auto-trait-selection.rs:7:16 + | +LL | fn is_trait, 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`. diff --git a/tests/ui/impl-trait/auto-trait-selection.rs b/tests/ui/impl-trait/auto-trait-selection.rs new file mode 100644 index 0000000000000..ee5612459c257 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-selection.rs @@ -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, U: Default>(_: T) -> U { + Default::default() +} + +trait Trait {} +impl Trait for T {} +impl Trait for T {} +fn foo() -> impl Sized { + if false { is_trait(foo()) } else { Default::default() } + //~^ ERROR: type annotations needed +} + +fn main() {} diff --git a/tests/ui/impl-trait/rpit/const_check_false_cycle.rs b/tests/ui/impl-trait/rpit/const_check_false_cycle.rs new file mode 100644 index 0000000000000..d4ea0e3b14785 --- /dev/null +++ b/tests/ui/impl-trait/rpit/const_check_false_cycle.rs @@ -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() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs index 21197fcaa273a..d78c7cada3385 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs @@ -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"; diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr index 2f8051109173d..1512029f0adef 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr @@ -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 || { | ^^^^^ @@ -23,7 +23,7 @@ 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` @@ -31,38 +31,7 @@ LL | assert_eq!(first, &b'f'); = 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`. diff --git a/tests/ui/type-alias-impl-trait/in-where-clause.rs b/tests/ui/type-alias-impl-trait/in-where-clause.rs index 7c0de39c7c91c..8fc05023147fd 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.rs +++ b/tests/ui/type-alias-impl-trait/in-where-clause.rs @@ -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] } diff --git a/tests/ui/type-alias-impl-trait/in-where-clause.stderr b/tests/ui/type-alias-impl-trait/in-where-clause.stderr index 9c08b8f127d27..197e0fc2c79ec 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.stderr +++ b/tests/ui/type-alias-impl-trait/in-where-clause.stderr @@ -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 @@ -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`.