Skip to content

Commit c7cb728

Browse files
committed
introduce a unit testing feature rustc_evaluate_where_clauses
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.
1 parent 506e75c commit c7cb728

File tree

6 files changed

+174
-3
lines changed

6 files changed

+174
-3
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
565565
template!(Word, List: "delay_span_bug_from_inside_query")
566566
),
567567
rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
568+
rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)),
568569
rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
569570
rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
570571
rustc_attr!(

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ symbols! {
10101010
rustc_dump_program_clauses,
10111011
rustc_dump_user_substs,
10121012
rustc_error,
1013+
rustc_evaluate_where_clauses,
10131014
rustc_expected_cgu_reuse,
10141015
rustc_if_this_changed,
10151016
rustc_inherit_overflow_checks,

compiler/rustc_typeck/src/check/callee.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
66
use rustc_hir as hir;
77
use rustc_hir::def::{Namespace, Res};
88
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
9-
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
10-
use rustc_infer::{infer, traits};
9+
use rustc_infer::{
10+
infer,
11+
traits::{self, Obligation},
12+
};
13+
use rustc_infer::{
14+
infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
15+
traits::ObligationCause,
16+
};
1117
use rustc_middle::ty::adjustment::{
1218
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
1319
};
@@ -17,6 +23,7 @@ use rustc_span::symbol::{sym, Ident};
1723
use rustc_span::Span;
1824
use rustc_target::spec::abi;
1925
use rustc_trait_selection::autoderef::Autoderef;
26+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
2027
use std::iter;
2128

2229
/// Checks that it is legal to call methods of the trait corresponding
@@ -294,7 +301,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
294301
expected: Expectation<'tcx>,
295302
) -> Ty<'tcx> {
296303
let (fn_sig, def_id) = match *callee_ty.kind() {
297-
ty::FnDef(def_id, _) => (callee_ty.fn_sig(self.tcx), Some(def_id)),
304+
ty::FnDef(def_id, subst) => {
305+
// Unit testing: function items annotated with
306+
// `#[rustc_evaluate_where_clauses]` trigger special output
307+
// to let us test the trait evaluation system.
308+
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
309+
let predicates = self.tcx.predicates_of(def_id);
310+
let predicates = predicates.instantiate(self.tcx, subst);
311+
for (predicate, predicate_span) in
312+
predicates.predicates.iter().zip(&predicates.spans)
313+
{
314+
let obligation = Obligation::new(
315+
ObligationCause::dummy_with_span(callee_expr.span),
316+
self.param_env,
317+
predicate.clone(),
318+
);
319+
let result = self.infcx.evaluate_obligation(&obligation);
320+
self.tcx
321+
.sess
322+
.struct_span_err(
323+
callee_expr.span,
324+
&format!("evaluate({:?}) = {:?}", predicate, result),
325+
)
326+
.span_label(*predicate_span, "predicate")
327+
.emit();
328+
}
329+
}
330+
(callee_ty.fn_sig(self.tcx), Some(def_id))
331+
}
298332
ty::FnPtr(sig) => (sig, None),
299333
ref t => {
300334
let mut unit_variant = None;

src/test/ui/cycle-me.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![feature(rustc_attrs)]
2+
3+
// A (reached depth 0)
4+
// ...
5+
// B // depth 1 -- reached depth = 0
6+
// C // depth 2 -- reached depth = 1 (should be 0)
7+
// B
8+
// A // depth 0
9+
// D (reached depth 1)
10+
// C (cache -- reached depth = 2)
11+
12+
struct A {
13+
b: B,
14+
c: C,
15+
}
16+
17+
struct B {
18+
c: C,
19+
a: Option<Box<A>>,
20+
}
21+
22+
struct C {
23+
b: Option<Box<B>>,
24+
}
25+
26+
#[rustc_evaluate_where_clauses]
27+
fn test<X: Send>() {}
28+
29+
fn main() {
30+
test::<A>();
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Regression test for issue #83538. The problem here is that we have
2+
// two cycles:
3+
//
4+
// * `Ty` embeds `Box<Ty>` indirectly, which depends on `Global: 'static`, which is OkModuloRegions.
5+
// * But `Ty` also references `First`, which has a cycle on itself. That should just be `Ok`.
6+
//
7+
// But our caching mechanism was blending both cycles and giving the incorrect result.
8+
9+
#![feature(rustc_attrs)]
10+
#![allow(bad_style)]
11+
12+
struct First {
13+
b: Vec<First>,
14+
}
15+
16+
pub struct Second {
17+
d: Vec<First>,
18+
}
19+
20+
struct Third<f> {
21+
g: Vec<f>,
22+
}
23+
24+
enum Ty {
25+
j(Fourth, Fifth, Sixth),
26+
}
27+
28+
struct Fourth {
29+
o: Vec<Ty>,
30+
}
31+
32+
struct Fifth {
33+
bounds: First,
34+
}
35+
36+
struct Sixth {
37+
p: Box<Ty>,
38+
}
39+
40+
#[rustc_evaluate_where_clauses]
41+
fn forward()
42+
where
43+
Vec<First>: Unpin,
44+
Third<Ty>: Unpin,
45+
{
46+
}
47+
48+
#[rustc_evaluate_where_clauses]
49+
fn reverse()
50+
where
51+
Third<Ty>: Unpin,
52+
Vec<First>: Unpin,
53+
{
54+
}
55+
56+
fn main() {
57+
// The only ERROR included here is the one that is totally wrong:
58+
59+
forward();
60+
//~^ ERROR evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
61+
//~| ERROR evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
62+
63+
reverse();
64+
//~^ ERROR evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
65+
//~| ERROR evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
2+
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5
3+
|
4+
LL | Vec<First>: Unpin,
5+
| ----- predicate
6+
...
7+
LL | forward();
8+
| ^^^^^^^
9+
10+
error: evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
11+
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:59:5
12+
|
13+
LL | Third<Ty>: Unpin,
14+
| ----- predicate
15+
...
16+
LL | forward();
17+
| ^^^^^^^
18+
19+
error: evaluate(Binder(TraitPredicate(<Third<Ty> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
20+
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5
21+
|
22+
LL | Third<Ty>: Unpin,
23+
| ----- predicate
24+
...
25+
LL | reverse();
26+
| ^^^^^^^
27+
28+
error: evaluate(Binder(TraitPredicate(<std::vec::Vec<First> as std::marker::Unpin>), [])) = Ok(EvaluatedToOkModuloRegions)
29+
--> $DIR/issue-83538-tainted-cache-after-cycle.rs:63:5
30+
|
31+
LL | Vec<First>: Unpin,
32+
| ----- predicate
33+
...
34+
LL | reverse();
35+
| ^^^^^^^
36+
37+
error: aborting due to 4 previous errors
38+

0 commit comments

Comments
 (0)