Skip to content

Commit

Permalink
introduce a unit testing feature rustc_evaluate_where_clauses
Browse files Browse the repository at this point in the history
This attribute will cause us to invoke evaluate on every where clause of an
invoked function and to generate an error with the result.

Without this, it is very difficult to observe the effects of invoking the trait
evaluator.
  • Loading branch information
nikomatsakis committed May 13, 2021
1 parent 506e75c commit c7cb728
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 3 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
40 changes: 37 additions & 3 deletions compiler/rustc_typeck/src/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions src/test/ui/cycle-me.rs
Original file line number Diff line number Diff line change
@@ -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<Box<A>>,
}

struct C {
b: Option<Box<B>>,
}

#[rustc_evaluate_where_clauses]
fn test<X: Send>() {}

fn main() {
test::<A>();
}
66 changes: 66 additions & 0 deletions src/test/ui/traits/issue-83538-tainted-cache-after-cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Regression test for issue #83538. The problem here is that we have
// two cycles:
//
// * `Ty` embeds `Box<Ty>` 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<First>,
}

pub struct Second {
d: Vec<First>,
}

struct Third<f> {
g: Vec<f>,
}

enum Ty {
j(Fourth, Fifth, Sixth),
}

struct Fourth {
o: Vec<Ty>,
}

struct Fifth {
bounds: First,
}

struct Sixth {
p: Box<Ty>,
}

#[rustc_evaluate_where_clauses]
fn forward()
where
Vec<First>: Unpin,
Third<Ty>: Unpin,
{
}

#[rustc_evaluate_where_clauses]
fn reverse()
where
Third<Ty>: Unpin,
Vec<First>: Unpin,
{
}

fn main() {
// The only ERROR included here is the one that is totally wrong:

forward();
//~^ ERROR evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
//~| ERROR evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)

reverse();
//~^ ERROR evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
//~| ERROR evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
}
38 changes: 38 additions & 0 deletions src/test/ui/traits/issue-83538-tainted-cache-after-cycle.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error: evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5
|
LL | Vec<First>: Unpin,
| ----- predicate
...
LL | forward();
| ^^^^^^^

error: evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5
|
LL | Third<Ty>: Unpin,
| ----- predicate
...
LL | forward();
| ^^^^^^^

error: evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5
|
LL | Third<Ty>: Unpin,
| ----- predicate
...
LL | reverse();
| ^^^^^^^

error: evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5
|
LL | Vec<First>: Unpin,
| ----- predicate
...
LL | reverse();
| ^^^^^^^

error: aborting due to 4 previous errors

0 comments on commit c7cb728

Please sign in to comment.