1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 typeck.rs, an introduction
15 The type checker is responsible for:
17 1. Determining the type of each expression
18 2. Resolving methods and traits
19 3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
20 Well, dear reader, read on)
22 The main entry point is `check_crate()`. Type checking operates in
25 1. The collect phase first passes over all items and determines their
26 type, without examining their "innards".
28 2. Variance inference then runs to compute the variance of each parameter
30 3. Coherence checks for overlapping or orphaned impls
32 4. Finally, the check phase then checks function bodies and so forth.
33 Within the check phase, we check each function body one at a time
34 (bodies of function expressions are checked as part of the
35 containing function). Inference is used to supply types wherever
36 they are unknown. The actual checking of a function itself has
37 several phases (check, regionck, writeback), as discussed in the
38 documentation for the `check` module.
40 The type checker is defined into various submodules which are documented
43 - astconv: converts the AST representation of types
44 into the `ty` representation
46 - collect: computes the types of each top-level item and enters them into
47 the `cx.tcache` table for later use
49 - coherence: enforces coherence rules, builds some tables
51 - variance: variance inference
53 - check: walks over function bodies and type checks them, inferring types for
54 local variables, type parameters, etc as necessary.
56 - infer: finds the types to use for each type variable such that
57 all subtyping and assignment constraints are met. In essence, the check
58 module specifies the constraints, and the infer module solves them.
62 #[allow(non_camel_case_types)];
68 use util::common::time;
69 use util::ppaux::Repr;
71 use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
73 use std::cell::RefCell;
75 use collections::List;
76 use syntax::codemap::Span;
77 use syntax::print::pprust::*;
78 use syntax::{ast, ast_map, abi};
88 #[deriving(Clone, Encodable, Decodable, Eq, Ord)]
89 pub enum param_index {
94 #[deriving(Clone, Encodable, Decodable)]
95 pub enum MethodOrigin {
96 // fully statically resolved method
97 MethodStatic(ast::DefId),
99 // method invoked on a type parameter with a bounded trait
100 MethodParam(MethodParam),
102 // method invoked on a trait instance
103 MethodObject(MethodObject),
107 // details for a method invoked with a receiver whose type is a type parameter
108 // with a bounded trait.
109 #[deriving(Clone, Encodable, Decodable)]
110 pub struct MethodParam {
111 // the trait containing the method to be invoked
112 trait_id: ast::DefId,
114 // index of the method to be invoked amongst the trait's methods
117 // index of the type parameter (from those that are in scope) that is
118 // the type of the receiver
119 param_num: param_index,
121 // index of the bound for this type parameter which specifies the trait
125 // details for a method invoked with a receiver whose type is an object
126 #[deriving(Clone, Encodable, Decodable)]
127 pub struct MethodObject {
128 // the (super)trait containing the method to be invoked
129 trait_id: ast::DefId,
131 // the actual base trait id of the object
132 object_trait_id: ast::DefId,
134 // index of the method to be invoked amongst the trait's methods
137 // index into the actual runtime vtable.
138 // the vtable is formed by concatenating together the method lists of
139 // the base object trait and all supertraits; this is the index into
145 pub struct MethodCallee {
146 origin: MethodOrigin,
151 #[deriving(Clone, Eq, Hash)]
152 pub struct MethodCall {
153 expr_id: ast::NodeId,
158 pub fn expr(id: ast::NodeId) -> MethodCall {
165 pub fn autoderef(expr_id: ast::NodeId, autoderef: u32) -> MethodCall {
168 autoderef: 1 + autoderef
173 // maps from an expression id that corresponds to a method call to the details
174 // of the method to be invoked
175 pub type MethodMap = @RefCell<FnvHashMap<MethodCall, MethodCallee>>;
177 pub type vtable_param_res = @Vec<vtable_origin> ;
178 // Resolutions for bounds of all parameters, left to right, for a given path.
179 pub type vtable_res = @Vec<vtable_param_res> ;
182 pub enum vtable_origin {
184 Statically known vtable. def_id gives the class or impl item
185 from whence comes the vtable, and tys are the type substs.
186 vtable_res is the vtable itself
188 vtable_static(ast::DefId, Vec<ty::t> , vtable_res),
191 Dynamic vtable, comes from a parameter that has a bound on it:
192 fn foo<T:quux,baz,bar>(a: T) -- a's vtable would have a
195 The first argument is the param index (identifying T in the example),
196 and the second is the bound number (identifying baz)
198 vtable_param(param_index, uint),
201 impl Repr for vtable_origin {
202 fn repr(&self, tcx: &ty::ctxt) -> ~str {
204 vtable_static(def_id, ref tys, ref vtable_res) => {
205 format!("vtable_static({:?}:{}, {}, {})",
207 ty::item_path_str(tcx, def_id),
209 vtable_res.repr(tcx))
212 vtable_param(x, y) => {
213 format!("vtable_param({:?}, {:?})", x, y)
219 pub type vtable_map = @RefCell<NodeMap<vtable_res>>;
222 // Information about the vtable resolutions for a trait impl.
223 // Mostly the information is important for implementing default
226 pub struct impl_res {
227 // resolutions for any bounded params on the trait definition
228 trait_vtables: vtable_res,
229 // resolutions for the trait /itself/ (and for supertraits)
230 self_vtables: vtable_param_res
233 impl Repr for impl_res {
234 fn repr(&self, tcx: &ty::ctxt) -> ~str {
235 format!("impl_res \\{trait_vtables={}, self_vtables={}\\}",
236 self.trait_vtables.repr(tcx),
237 self.self_vtables.repr(tcx))
241 pub type impl_vtable_map = RefCell<DefIdMap<impl_res>>;
243 pub struct CrateCtxt<'a> {
244 // A mapping from method call sites to traits that have that method.
245 trait_map: resolve::TraitMap,
246 method_map: MethodMap,
247 vtable_map: vtable_map,
251 // Functions that write types into the node type table
252 pub fn write_ty_to_tcx(tcx: &ty::ctxt, node_id: ast::NodeId, ty: ty::t) {
253 debug!("write_ty_to_tcx({}, {})", node_id, ppaux::ty_to_str(tcx, ty));
254 assert!(!ty::type_needs_infer(ty));
255 let mut node_types = tcx.node_types.borrow_mut();
256 node_types.get().insert(node_id as uint, ty);
258 pub fn write_substs_to_tcx(tcx: &ty::ctxt,
259 node_id: ast::NodeId,
260 substs: Vec<ty::t> ) {
261 if substs.len() > 0u {
262 debug!("write_substs_to_tcx({}, {:?})", node_id,
263 substs.map(|t| ppaux::ty_to_str(tcx, *t)));
264 assert!(substs.iter().all(|t| !ty::type_needs_infer(*t)));
266 let mut node_type_substs = tcx.node_type_substs.borrow_mut();
267 node_type_substs.get().insert(node_id, substs);
270 pub fn write_tpt_to_tcx(tcx: &ty::ctxt,
271 node_id: ast::NodeId,
272 tpt: &ty::ty_param_substs_and_ty) {
273 write_ty_to_tcx(tcx, node_id, tpt.ty);
274 if !tpt.substs.tps.is_empty() {
275 write_substs_to_tcx(tcx, node_id, tpt.substs.tps.clone());
279 pub fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> ast::Def {
280 let def_map = tcx.def_map.borrow();
281 match def_map.get().find(&id) {
284 tcx.sess.span_fatal(sp, "internal error looking up a definition")
289 pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId)
291 lookup_def_tcx(ccx.tcx, sp, id)
294 pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
295 ty::ty_param_bounds_and_ty {
296 generics: ty::Generics {type_param_defs: Rc::new(Vec::new()),
297 region_param_defs: Rc::new(Vec::new())},
302 pub fn require_same_types(tcx: &ty::ctxt,
303 maybe_infcx: Option<&infer::InferCtxt>,
304 t1_is_expected: bool,
310 let result = match maybe_infcx {
312 let infcx = infer::new_infer_ctxt(tcx);
313 infer::mk_eqty(&infcx, t1_is_expected, infer::Misc(span), t1, t2)
316 infer::mk_eqty(infcx, t1_is_expected, infer::Misc(span), t1, t2)
323 tcx.sess.span_err(span, msg() + ": " +
324 ty::type_err_to_str(tcx, terr));
325 ty::note_and_explain_type_err(tcx, terr);
331 // a list of mapping from in-scope-region-names ("isr") to the
332 // corresponding ty::Region
333 pub type isr_alist = @List<(ty::BoundRegion, ty::Region)>;
335 trait get_region<'a, T:'static> {
336 fn get(&'a self, br: ty::BoundRegion) -> ty::Region;
339 impl<'a, T:'static> get_region <'a, T> for isr_alist {
340 fn get(&'a self, br: ty::BoundRegion) -> ty::Region {
341 let mut region = None;
342 for isr in self.iter() {
343 let (isr_br, isr_r) = *isr;
344 if isr_br == br { region = Some(isr_r); break; }
350 fn check_main_fn_ty(ccx: &CrateCtxt,
351 main_id: ast::NodeId,
354 let main_t = ty::node_id_to_type(tcx, main_id);
355 match ty::get(main_t).sty {
356 ty::ty_bare_fn(..) => {
357 match tcx.map.find(main_id) {
358 Some(ast_map::NodeItem(it)) => {
360 ast::ItemFn(_, _, _, ref ps, _)
361 if ps.is_parameterized() => {
364 "main function is not allowed to have type parameters");
372 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
373 purity: ast::ImpureFn,
374 abis: abi::AbiSet::Rust(),
378 output: ty::mk_nil(),
383 require_same_types(tcx, None, false, main_span, main_t, se_ty,
384 || format!("main function expects type: `{}`",
385 ppaux::ty_to_str(ccx.tcx, se_ty)));
388 tcx.sess.span_bug(main_span,
389 format!("main has a non-function type: found `{}`",
390 ppaux::ty_to_str(tcx, main_t)));
395 fn check_start_fn_ty(ccx: &CrateCtxt,
396 start_id: ast::NodeId,
399 let start_t = ty::node_id_to_type(tcx, start_id);
400 match ty::get(start_t).sty {
401 ty::ty_bare_fn(_) => {
402 match tcx.map.find(start_id) {
403 Some(ast_map::NodeItem(it)) => {
405 ast::ItemFn(_,_,_,ref ps,_)
406 if ps.is_parameterized() => {
409 "start function is not allowed to have type parameters");
418 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
419 purity: ast::ImpureFn,
420 abis: abi::AbiSet::Rust(),
425 ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
427 output: ty::mk_int(),
432 require_same_types(tcx, None, false, start_span, start_t, se_ty,
433 || format!("start function expects type: `{}`", ppaux::ty_to_str(ccx.tcx, se_ty)));
437 tcx.sess.span_bug(start_span,
438 format!("start has a non-function type: found `{}`",
439 ppaux::ty_to_str(tcx, start_t)));
444 fn check_for_entry_fn(ccx: &CrateCtxt) {
446 if !tcx.sess.building_library.get() {
447 match tcx.sess.entry_fn.get() {
448 Some((id, sp)) => match tcx.sess.entry_type.get() {
449 Some(session::EntryMain) => check_main_fn_ty(ccx, id, sp),
450 Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp),
451 Some(session::EntryNone) => {}
452 None => tcx.sess.bug("entry function without a type")
459 pub fn check_crate(tcx: &ty::ctxt,
460 trait_map: resolve::TraitMap,
462 -> (MethodMap, vtable_map) {
463 let time_passes = tcx.sess.time_passes();
464 let ccx = CrateCtxt {
465 trait_map: trait_map,
466 method_map: @RefCell::new(FnvHashMap::new()),
467 vtable_map: @RefCell::new(NodeMap::new()),
471 time(time_passes, "type collecting", (), |_|
472 collect::collect_item_types(&ccx, krate));
474 // this ensures that later parts of type checking can assume that items
475 // have valid types and not error
476 tcx.sess.abort_if_errors();
478 time(time_passes, "variance inference", (), |_|
479 variance::infer_variance(tcx, krate));
481 time(time_passes, "coherence checking", (), |_|
482 coherence::check_coherence(&ccx, krate));
484 time(time_passes, "type checking", (), |_|
485 check::check_item_types(&ccx, krate));
487 check_for_entry_fn(&ccx);
488 tcx.sess.abort_if_errors();
489 (ccx.method_map, ccx.vtable_map)