]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/test.rs
Ensure `record_layout_for_printing()` is inlined.
[rust.git] / src / librustc_driver / test.rs
1 //! Standalone tests for the inference module.
2
3 use driver;
4 use errors;
5 use errors::emitter::Emitter;
6 use errors::{DiagnosticBuilder, Level};
7 use rustc::hir;
8 use rustc::hir::map as hir_map;
9 use rustc::infer::outlives::env::OutlivesEnvironment;
10 use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors};
11 use rustc::middle::region;
12 use rustc::session::config::{OutputFilenames, OutputTypes};
13 use rustc::session::{self, config};
14 use rustc::traits::ObligationCause;
15 use rustc::ty::query::OnDiskCache;
16 use rustc::ty::subst::Subst;
17 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
18 use rustc_data_structures::sync::{self, Lrc};
19 use rustc_lint;
20 use rustc_metadata::cstore::CStore;
21 use rustc_target::spec::abi::Abi;
22 use syntax;
23 use syntax::ast;
24 use syntax::feature_gate::UnstableFeatures;
25 use syntax::source_map::{FileName, FilePathMapping, SourceMap};
26 use syntax::symbol::Symbol;
27
28 use std::path::PathBuf;
29 use std::sync::mpsc;
30
31 struct Env<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
32     infcx: &'a infer::InferCtxt<'a, 'gcx, 'tcx>,
33     region_scope_tree: &'a mut region::ScopeTree,
34     param_env: ty::ParamEnv<'tcx>,
35 }
36
37 struct RH<'a> {
38     id: hir::ItemLocalId,
39     sub: &'a [RH<'a>],
40 }
41
42 const EMPTY_SOURCE_STR: &'static str = "#![feature(no_core)] #![no_core]";
43
44 struct ExpectErrorEmitter {
45     messages: Vec<String>,
46 }
47
48 fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
49     match lvl {
50         Level::Bug | Level::Fatal | Level::Error => {}
51         _ => {
52             return;
53         }
54     }
55
56     debug!("Error: {}", msg);
57     match e.messages.iter().position(|m| msg.contains(m)) {
58         Some(i) => {
59             e.messages.remove(i);
60         }
61         None => {
62             debug!("Unexpected error: {} Expected: {:?}", msg, e.messages);
63             panic!("Unexpected error: {} Expected: {:?}", msg, e.messages);
64         }
65     }
66 }
67
68 impl Emitter for ExpectErrorEmitter {
69     fn emit(&mut self, db: &DiagnosticBuilder) {
70         remove_message(self, &db.message(), db.level);
71         for child in &db.children {
72             remove_message(self, &child.message(), child.level);
73         }
74     }
75 }
76
77 fn errors(msgs: &[&str]) -> (Box<dyn Emitter + sync::Send>, usize) {
78     let v = msgs.iter().map(|m| m.to_string()).collect();
79     (
80         box ExpectErrorEmitter { messages: v } as Box<dyn Emitter + sync::Send>,
81         msgs.len(),
82     )
83 }
84
85 fn test_env<F>(source_string: &str, args: (Box<dyn Emitter + sync::Send>, usize), body: F)
86 where
87     F: FnOnce(Env) + sync::Send,
88 {
89     syntax::with_globals(|| {
90         let mut options = config::Options::default();
91         options.debugging_opts.verbose = true;
92         options.unstable_features = UnstableFeatures::Allow;
93
94         driver::spawn_thread_pool(options, |options| {
95             test_env_with_pool(options, source_string, args, body)
96         })
97     });
98 }
99
100 fn test_env_with_pool<F>(
101     options: config::Options,
102     source_string: &str,
103     (emitter, expected_err_count): (Box<dyn Emitter + sync::Send>, usize),
104     body: F,
105 ) where
106     F: FnOnce(Env),
107 {
108     let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
109     let sess = session::build_session_(
110         options,
111         None,
112         diagnostic_handler,
113         Lrc::new(SourceMap::new(FilePathMapping::empty())),
114     );
115     let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader());
116     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
117     let input = config::Input::Str {
118         name: FileName::anon_source_code(&source_string),
119         input: source_string.to_string(),
120     };
121     let krate =
122         driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input).unwrap();
123     let driver::ExpansionResult {
124         defs,
125         resolutions,
126         mut hir_forest,
127         ..
128     } = {
129         driver::phase_2_configure_and_expand(
130             &sess,
131             &cstore,
132             krate,
133             None,
134             "test",
135             None,
136             |_| Ok(()),
137         ).expect("phase 2 aborted")
138     };
139
140     let mut arenas = ty::AllArenas::new();
141     let hir_map = hir_map::map_crate(&sess, &cstore, &mut hir_forest, &defs);
142
143     // Run just enough stuff to build a tcx.
144     let (tx, _rx) = mpsc::channel();
145     let outputs = OutputFilenames {
146         out_directory: PathBuf::new(),
147         out_filestem: String::new(),
148         single_output_file: None,
149         extra: String::new(),
150         outputs: OutputTypes::new(&[]),
151     };
152     TyCtxt::create_and_enter(
153         &sess,
154         &cstore,
155         ty::query::Providers::default(),
156         ty::query::Providers::default(),
157         &mut arenas,
158         resolutions,
159         hir_map,
160         OnDiskCache::new_empty(sess.source_map()),
161         "test_crate",
162         tx,
163         &outputs,
164         |tcx| {
165             tcx.infer_ctxt().enter(|infcx| {
166                 let mut region_scope_tree = region::ScopeTree::default();
167                 let param_env = ty::ParamEnv::empty();
168                 body(Env {
169                     infcx: &infcx,
170                     region_scope_tree: &mut region_scope_tree,
171                     param_env: param_env,
172                 });
173                 let outlives_env = OutlivesEnvironment::new(param_env);
174                 let def_id = tcx.hir().local_def_id(ast::CRATE_NODE_ID);
175                 infcx.resolve_regions_and_report_errors(
176                     def_id,
177                     &region_scope_tree,
178                     &outlives_env,
179                     SuppressRegionErrors::default(),
180                 );
181                 assert_eq!(tcx.sess.err_count(), expected_err_count);
182             });
183         },
184     );
185 }
186
187 fn d1() -> ty::DebruijnIndex {
188     ty::INNERMOST
189 }
190
191 fn d2() -> ty::DebruijnIndex {
192     d1().shifted_in(1)
193 }
194
195 impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
196     pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
197         self.infcx.tcx
198     }
199
200     pub fn create_region_hierarchy(
201         &mut self,
202         rh: &RH,
203         parent: (region::Scope, region::ScopeDepth),
204     ) {
205         let me = region::Scope {
206             id: rh.id,
207             data: region::ScopeData::Node,
208         };
209         self.region_scope_tree.record_scope_parent(me, Some(parent));
210         for child_rh in rh.sub {
211             self.create_region_hierarchy(child_rh, (me, parent.1 + 1));
212         }
213     }
214
215     pub fn create_simple_region_hierarchy(&mut self) {
216         // Creates a region hierarchy where 1 is root, 10 and 11 are
217         // children of 1, etc.
218
219         let dscope = region::Scope {
220             id: hir::ItemLocalId::from_u32(1),
221             data: region::ScopeData::Destruction,
222         };
223         self.region_scope_tree.record_scope_parent(dscope, None);
224         self.create_region_hierarchy(
225             &RH {
226                 id: hir::ItemLocalId::from_u32(1),
227                 sub: &[
228                     RH {
229                         id: hir::ItemLocalId::from_u32(10),
230                         sub: &[],
231                     },
232                     RH {
233                         id: hir::ItemLocalId::from_u32(11),
234                         sub: &[],
235                     },
236                 ],
237             },
238             (dscope, 1),
239         );
240     }
241
242     #[allow(dead_code)] // this seems like it could be useful, even if we don't use it now
243     pub fn lookup_item(&self, names: &[String]) -> ast::NodeId {
244         return match search_mod(self, &self.infcx.tcx.hir().krate().module, 0, names) {
245             Some(id) => id,
246             None => {
247                 panic!("no item found: `{}`", names.join("::"));
248             }
249         };
250
251         fn search_mod(
252             this: &Env,
253             m: &hir::Mod,
254             idx: usize,
255             names: &[String],
256         ) -> Option<ast::NodeId> {
257             assert!(idx < names.len());
258             for item in &m.item_ids {
259                 let item = this.infcx.tcx.hir().expect_item(item.id);
260                 if item.ident.to_string() == names[idx] {
261                     return search(this, item, idx + 1, names);
262                 }
263             }
264             return None;
265         }
266
267         fn search(this: &Env, it: &hir::Item, idx: usize, names: &[String]) -> Option<ast::NodeId> {
268             if idx == names.len() {
269                 return Some(it.id);
270             }
271
272             return match it.node {
273                 hir::ItemKind::Use(..)
274                 | hir::ItemKind::ExternCrate(..)
275                 | hir::ItemKind::Const(..)
276                 | hir::ItemKind::Static(..)
277                 | hir::ItemKind::Fn(..)
278                 | hir::ItemKind::ForeignMod(..)
279                 | hir::ItemKind::GlobalAsm(..)
280                 | hir::ItemKind::Existential(..)
281                 | hir::ItemKind::Ty(..) => None,
282
283                 hir::ItemKind::Enum(..)
284                 | hir::ItemKind::Struct(..)
285                 | hir::ItemKind::Union(..)
286                 | hir::ItemKind::Trait(..)
287                 | hir::ItemKind::TraitAlias(..)
288                 | hir::ItemKind::Impl(..) => None,
289
290                 hir::ItemKind::Mod(ref m) => search_mod(this, m, idx, names),
291             };
292         }
293     }
294
295     pub fn make_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
296         match self.infcx
297             .at(&ObligationCause::dummy(), self.param_env)
298             .sub(a, b)
299         {
300             Ok(_) => true,
301             Err(ref e) => panic!("Encountered error: {}", e),
302         }
303     }
304
305     pub fn is_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
306         self.infcx.can_sub(self.param_env, a, b).is_ok()
307     }
308
309     pub fn assert_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) {
310         if !self.is_subtype(a, b) {
311             panic!("{} is not a subtype of {}, but it should be", a, b);
312         }
313     }
314
315     pub fn assert_eq(&self, a: Ty<'tcx>, b: Ty<'tcx>) {
316         self.assert_subtype(a, b);
317         self.assert_subtype(b, a);
318     }
319
320     pub fn t_fn(&self, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>) -> Ty<'tcx> {
321         self.infcx
322             .tcx
323             .mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig(
324                 input_tys.iter().cloned(),
325                 output_ty,
326                 false,
327                 hir::Unsafety::Normal,
328                 Abi::Rust,
329             )))
330     }
331
332     pub fn t_nil(&self) -> Ty<'tcx> {
333         self.infcx.tcx.mk_unit()
334     }
335
336     pub fn t_pair(&self, ty1: Ty<'tcx>, ty2: Ty<'tcx>) -> Ty<'tcx> {
337         self.infcx.tcx.intern_tup(&[ty1, ty2])
338     }
339
340     pub fn t_param(&self, index: u32) -> Ty<'tcx> {
341         let name = format!("T{}", index);
342         self.infcx
343             .tcx
344             .mk_ty_param(index, Symbol::intern(&name).as_interned_str())
345     }
346
347     pub fn re_early_bound(&self, index: u32, name: &'static str) -> ty::Region<'tcx> {
348         let name = Symbol::intern(name).as_interned_str();
349         self.infcx
350             .tcx
351             .mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
352                 def_id: self.infcx.tcx.hir().local_def_id(ast::CRATE_NODE_ID),
353                 index,
354                 name,
355             }))
356     }
357
358     pub fn re_late_bound_with_debruijn(
359         &self,
360         id: u32,
361         debruijn: ty::DebruijnIndex,
362     ) -> ty::Region<'tcx> {
363         self.infcx
364             .tcx
365             .mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id)))
366     }
367
368     pub fn t_rptr(&self, r: ty::Region<'tcx>) -> Ty<'tcx> {
369         self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
370     }
371
372     pub fn t_rptr_late_bound(&self, id: u32) -> Ty<'tcx> {
373         let r = self.re_late_bound_with_debruijn(id, d1());
374         self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
375     }
376
377     pub fn t_rptr_late_bound_with_debruijn(
378         &self,
379         id: u32,
380         debruijn: ty::DebruijnIndex,
381     ) -> Ty<'tcx> {
382         let r = self.re_late_bound_with_debruijn(id, debruijn);
383         self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
384     }
385
386     pub fn t_rptr_scope(&self, id: u32) -> Ty<'tcx> {
387         let r = ty::ReScope(region::Scope {
388             id: hir::ItemLocalId::from_u32(id),
389             data: region::ScopeData::Node,
390         });
391         self.infcx
392             .tcx
393             .mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize)
394     }
395
396     pub fn re_free(&self, id: u32) -> ty::Region<'tcx> {
397         self.infcx.tcx.mk_region(ty::ReFree(ty::FreeRegion {
398             scope: self.infcx.tcx.hir().local_def_id(ast::CRATE_NODE_ID),
399             bound_region: ty::BrAnon(id),
400         }))
401     }
402
403     pub fn t_rptr_free(&self, id: u32) -> Ty<'tcx> {
404         let r = self.re_free(id);
405         self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
406     }
407
408     pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> {
409         self.infcx
410             .at(&ObligationCause::dummy(), self.param_env)
411             .sub(t1, t2)
412     }
413
414     /// Checks that `t1 <: t2` is true (this may register additional
415     /// region checks).
416     pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
417         match self.sub(t1, t2) {
418             Ok(InferOk {
419                 obligations,
420                 value: (),
421             }) => {
422                 // None of these tests should require nested obligations.
423                 assert!(obligations.is_empty());
424             }
425             Err(ref e) => {
426                 panic!("unexpected error computing sub({:?},{:?}): {}", t1, t2, e);
427             }
428         }
429     }
430 }
431
432 #[test]
433 fn contravariant_region_ptr_ok() {
434     test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
435         env.create_simple_region_hierarchy();
436         let t_rptr1 = env.t_rptr_scope(1);
437         let t_rptr10 = env.t_rptr_scope(10);
438         env.assert_eq(t_rptr1, t_rptr1);
439         env.assert_eq(t_rptr10, t_rptr10);
440         env.make_subtype(t_rptr1, t_rptr10);
441     })
442 }
443
444 #[test]
445 fn contravariant_region_ptr_err() {
446     test_env(EMPTY_SOURCE_STR, errors(&["mismatched types"]), |mut env| {
447         env.create_simple_region_hierarchy();
448         let t_rptr1 = env.t_rptr_scope(1);
449         let t_rptr10 = env.t_rptr_scope(10);
450         env.assert_eq(t_rptr1, t_rptr1);
451         env.assert_eq(t_rptr10, t_rptr10);
452
453         // This will cause an error when regions are resolved.
454         env.make_subtype(t_rptr10, t_rptr1);
455     })
456 }
457
458 #[test]
459 fn sub_bound_free_true() {
460     //! Test that:
461     //!
462     //!     for<'a> fn(&'a isize) <: fn(&'b isize)
463     //!
464     //! *does* hold.
465
466     test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
467         env.create_simple_region_hierarchy();
468         let t_rptr_bound1 = env.t_rptr_late_bound(1);
469         let t_rptr_free1 = env.t_rptr_free(1);
470         env.check_sub(
471             env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
472             env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
473         );
474     })
475 }
476
477 /// Test substituting a bound region into a function, which introduces another level of binding.
478 /// This requires adjusting the Debruijn index.
479 #[test]
480 fn subst_ty_renumber_bound() {
481     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
482         // Situation:
483         // Theta = [A -> &'a foo]
484
485         let t_rptr_bound1 = env.t_rptr_late_bound(1);
486
487         // t_source = fn(A)
488         let t_source = {
489             let t_param = env.t_param(0);
490             env.t_fn(&[t_param], env.t_nil())
491         };
492
493         let substs = env.infcx.tcx.intern_substs(&[t_rptr_bound1.into()]);
494         let t_substituted = t_source.subst(env.infcx.tcx, substs);
495
496         // t_expected = fn(&'a isize)
497         let t_expected = {
498             let t_ptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, d2());
499             env.t_fn(&[t_ptr_bound2], env.t_nil())
500         };
501
502         debug!(
503             "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
504             t_source, substs, t_substituted, t_expected
505         );
506
507         assert_eq!(t_substituted, t_expected);
508     })
509 }
510
511 /// Tests substituting a bound region into a function, which introduces another level of binding.
512 /// This requires adjusting the De Bruijn index.
513 #[test]
514 fn subst_ty_renumber_some_bounds() {
515     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
516         // Situation:
517         // `Theta = [A -> &'a foo]`
518
519         let t_rptr_bound1 = env.t_rptr_late_bound(1);
520
521         // `t_source = (A, fn(A))`
522         let t_source = {
523             let t_param = env.t_param(0);
524             env.t_pair(t_param, env.t_fn(&[t_param], env.t_nil()))
525         };
526
527         let substs = env.infcx.tcx.intern_substs(&[t_rptr_bound1.into()]);
528         let t_substituted = t_source.subst(env.infcx.tcx, substs);
529
530         // `t_expected = (&'a isize, fn(&'a isize))`
531         //
532         // However, note that the Debruijn index is different in the different cases.
533         let t_expected = {
534             let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, d2());
535             env.t_pair(t_rptr_bound1, env.t_fn(&[t_rptr_bound2], env.t_nil()))
536         };
537
538         debug!(
539             "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
540             t_source, substs, t_substituted, t_expected
541         );
542
543         assert_eq!(t_substituted, t_expected);
544     })
545 }
546
547 /// Tests that we correctly compute whether a type has escaping regions or not.
548 #[test]
549 fn escaping() {
550     test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
551         // Situation:
552         // `Theta = [A -> &'a foo]`
553         env.create_simple_region_hierarchy();
554
555         assert!(!env.t_nil().has_escaping_bound_vars());
556
557         let t_rptr_free1 = env.t_rptr_free(1);
558         assert!(!t_rptr_free1.has_escaping_bound_vars());
559
560         let t_rptr_bound1 = env.t_rptr_late_bound_with_debruijn(1, d1());
561         assert!(t_rptr_bound1.has_escaping_bound_vars());
562
563         let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, d2());
564         assert!(t_rptr_bound2.has_escaping_bound_vars());
565
566         // `t_fn = fn(A)`
567         let t_param = env.t_param(0);
568         assert!(!t_param.has_escaping_bound_vars());
569         let t_fn = env.t_fn(&[t_param], env.t_nil());
570         assert!(!t_fn.has_escaping_bound_vars());
571     })
572 }
573
574 /// Tests applying a substitution where the value being substituted for an early-bound region is a
575 /// late-bound region.
576 #[test]
577 fn subst_region_renumber_region() {
578     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
579         let re_bound1 = env.re_late_bound_with_debruijn(1, d1());
580
581         // `type t_source<'a> = fn(&'a isize)`
582         let t_source = {
583             let re_early = env.re_early_bound(0, "'a");
584             env.t_fn(&[env.t_rptr(re_early)], env.t_nil())
585         };
586
587         let substs = env.infcx.tcx.intern_substs(&[re_bound1.into()]);
588         let t_substituted = t_source.subst(env.infcx.tcx, substs);
589
590         // `t_expected = fn(&'a isize)`
591         //
592         // but not that the Debruijn index is different in the different cases.
593         let t_expected = {
594             let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, d2());
595             env.t_fn(&[t_rptr_bound2], env.t_nil())
596         };
597
598         debug!(
599             "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
600             t_source, substs, t_substituted, t_expected
601         );
602
603         assert_eq!(t_substituted, t_expected);
604     })
605 }
606
607 #[test]
608 fn walk_ty() {
609     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
610         let tcx = env.infcx.tcx;
611         let int_ty = tcx.types.isize;
612         let usize_ty = tcx.types.usize;
613         let tup1_ty = tcx.intern_tup(&[int_ty, usize_ty, int_ty, usize_ty]);
614         let tup2_ty = tcx.intern_tup(&[tup1_ty, tup1_ty, usize_ty]);
615         let walked: Vec<_> = tup2_ty.walk().collect();
616         assert_eq!(
617             walked,
618             [
619                 tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty, usize_ty,
620                 int_ty, usize_ty, usize_ty
621             ]
622         );
623     })
624 }
625
626 #[test]
627 fn walk_ty_skip_subtree() {
628     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
629         let tcx = env.infcx.tcx;
630         let int_ty = tcx.types.isize;
631         let usize_ty = tcx.types.usize;
632         let tup1_ty = tcx.intern_tup(&[int_ty, usize_ty, int_ty, usize_ty]);
633         let tup2_ty = tcx.intern_tup(&[tup1_ty, tup1_ty, usize_ty]);
634
635         // types we expect to see (in order), plus a boolean saying
636         // whether to skip the subtree.
637         let mut expected = vec![
638             (tup2_ty, false),
639             (tup1_ty, false),
640             (int_ty, false),
641             (usize_ty, false),
642             (int_ty, false),
643             (usize_ty, false),
644             (tup1_ty, true), // skip the isize/usize/isize/usize
645             (usize_ty, false),
646         ];
647         expected.reverse();
648
649         let mut walker = tup2_ty.walk();
650         while let Some(t) = walker.next() {
651             debug!("walked to {:?}", t);
652             let (expected_ty, skip) = expected.pop().unwrap();
653             assert_eq!(t, expected_ty);
654             if skip {
655                 walker.skip_current_subtree();
656             }
657         }
658
659         assert!(expected.is_empty());
660     })
661 }