diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a8719be84c2a4..f54e090e4e05e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -565,6 +565,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word, List: "delay_span_bug_from_inside_query") ), rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)), + rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)), rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")), rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")), rustc_attr!( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4c80b84e3d275..93cb34999c3a7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1010,6 +1010,7 @@ symbols! { rustc_dump_program_clauses, rustc_dump_user_substs, rustc_error, + rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, rustc_if_this_changed, rustc_inherit_overflow_checks, diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index b48102e0fc9b8..cb8f336721ad6 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -6,8 +6,14 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::{infer, traits}; +use rustc_infer::{ + infer, + traits::{self, Obligation}, +}; +use rustc_infer::{ + infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + traits::ObligationCause, +}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; @@ -17,6 +23,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use std::iter; /// Checks that it is legal to call methods of the trait corresponding @@ -294,7 +301,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { - ty::FnDef(def_id, _) => (callee_ty.fn_sig(self.tcx), Some(def_id)), + ty::FnDef(def_id, subst) => { + // Unit testing: function items annotated with + // `#[rustc_evaluate_where_clauses]` trigger special output + // to let us test the trait evaluation system. + if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { + let predicates = self.tcx.predicates_of(def_id); + let predicates = predicates.instantiate(self.tcx, subst); + for (predicate, predicate_span) in + predicates.predicates.iter().zip(&predicates.spans) + { + let obligation = Obligation::new( + ObligationCause::dummy_with_span(callee_expr.span), + self.param_env, + predicate.clone(), + ); + let result = self.infcx.evaluate_obligation(&obligation); + self.tcx + .sess + .struct_span_err( + callee_expr.span, + &format!("evaluate({:?}) = {:?}", predicate, result), + ) + .span_label(*predicate_span, "predicate") + .emit(); + } + } + (callee_ty.fn_sig(self.tcx), Some(def_id)) + } ty::FnPtr(sig) => (sig, None), ref t => { let mut unit_variant = None; diff --git a/src/test/ui/cycle-me.rs b/src/test/ui/cycle-me.rs new file mode 100644 index 0000000000000..eb1ffe51ffcdd --- /dev/null +++ b/src/test/ui/cycle-me.rs @@ -0,0 +1,31 @@ +#![feature(rustc_attrs)] + +// A (reached depth 0) +// ... +// B // depth 1 -- reached depth = 0 +// C // depth 2 -- reached depth = 1 (should be 0) +// B +// A // depth 0 +// D (reached depth 1) +// C (cache -- reached depth = 2) + +struct A { + b: B, + c: C, +} + +struct B { + c: C, + a: Option>, +} + +struct C { + b: Option>, +} + +#[rustc_evaluate_where_clauses] +fn test() {} + +fn main() { + test::(); +} diff --git a/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.rs b/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.rs new file mode 100644 index 0000000000000..1b357575cb8b3 --- /dev/null +++ b/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.rs @@ -0,0 +1,66 @@ +// Regression test for issue #83538. The problem here is that we have +// two cycles: +// +// * `Ty` embeds `Box` indirectly, which depends on `Global: 'static`, which is OkModuloRegions. +// * But `Ty` also references `First`, which has a cycle on itself. That should just be `Ok`. +// +// But our caching mechanism was blending both cycles and giving the incorrect result. + +#![feature(rustc_attrs)] +#![allow(bad_style)] + +struct First { + b: Vec, +} + +pub struct Second { + d: Vec, +} + +struct Third { + g: Vec, +} + +enum Ty { + j(Fourth, Fifth, Sixth), +} + +struct Fourth { + o: Vec, +} + +struct Fifth { + bounds: First, +} + +struct Sixth { + p: Box, +} + +#[rustc_evaluate_where_clauses] +fn forward() +where + Vec: Unpin, + Third: Unpin, +{ +} + +#[rustc_evaluate_where_clauses] +fn reverse() +where + Third: Unpin, + Vec: Unpin, +{ +} + +fn main() { + // The only ERROR included here is the one that is totally wrong: + + forward(); + //~^ ERROR evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + //~| ERROR evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + + reverse(); + //~^ ERROR evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + //~| ERROR evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) +} diff --git a/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.stderr b/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.stderr new file mode 100644 index 0000000000000..82e00c3b97f4f --- /dev/null +++ b/src/test/ui/traits/issue-83538-tainted-cache-after-cycle.stderr @@ -0,0 +1,38 @@ +error: evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + --> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5 + | +LL | Vec: Unpin, + | ----- predicate +... +LL | forward(); + | ^^^^^^^ + +error: evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + --> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5 + | +LL | Third: Unpin, + | ----- predicate +... +LL | forward(); + | ^^^^^^^ + +error: evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + --> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5 + | +LL | Third: Unpin, + | ----- predicate +... +LL | reverse(); + | ^^^^^^^ + +error: evaluate(Binder(TraitPredicate( as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions) + --> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5 + | +LL | Vec: Unpin, + | ----- predicate +... +LL | reverse(); + | ^^^^^^^ + +error: aborting due to 4 previous errors +