/src/librustc_typeck/check/mod.rs
Rust | 4689 lines | 3488 code | 452 blank | 749 comment | 400 complexity | 9b4383ca6555cbae709d474ecb8fd3d0 MD5 | raw file
Possible License(s): AGPL-1.0, BSD-2-Clause, 0BSD, Apache-2.0, MIT, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
- // file at the top-level directory of this distribution and at
- // http://rust-lang.org/COPYRIGHT.
- //
- // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
- // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
- // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
- // option. This file may not be copied, modified, or distributed
- // except according to those terms.
- /*
- # check.rs
- Within the check phase of type check, we check each item one at a time
- (bodies of function expressions are checked as part of the containing
- function). Inference is used to supply types wherever they are
- unknown.
- By far the most complex case is checking the body of a function. This
- can be broken down into several distinct phases:
- - gather: creates type variables to represent the type of each local
- variable and pattern binding.
- - main: the main pass does the lion's share of the work: it
- determines the types of all expressions, resolves
- methods, checks for most invalid conditions, and so forth. In
- some cases, where a type is unknown, it may create a type or region
- variable and use that as the type of an expression.
- In the process of checking, various constraints will be placed on
- these type variables through the subtyping relationships requested
- through the `demand` module. The `infer` module is in charge
- of resolving those constraints.
- - regionck: after main is complete, the regionck pass goes over all
- types looking for regions and making sure that they did not escape
- into places they are not in scope. This may also influence the
- final assignments of the various region variables if there is some
- flexibility.
- - vtable: find and records the impls to use for each trait bound that
- appears on a type parameter.
- - writeback: writes the final types within a function body, replacing
- type variables with their final inferred types. These final types
- are written into the `tcx.node_types` table, which should *never* contain
- any reference to a type variable.
- ## Intermediate types
- While type checking a function, the intermediate types for the
- expressions, blocks, and so forth contained within the function are
- stored in `fcx.node_types` and `fcx.item_substs`. These types
- may contain unresolved type variables. After type checking is
- complete, the functions in the writeback module are used to take the
- types from this table, resolve them, and then write them into their
- permanent home in the type context `tcx`.
- This means that during inferencing you should use `fcx.write_ty()`
- and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
- nodes within the function.
- The types of top-level items, which never contain unbound type
- variables, are stored directly into the `tcx` tables.
- n.b.: A type variable is not the same thing as a type parameter. A
- type variable is rather an "instance" of a type parameter: that is,
- given a generic function `fn foo<T>(t: T)`: while checking the
- function `foo`, the type `ty_param(0)` refers to the type `T`, which
- is treated in abstract. When `foo()` is called, however, `T` will be
- substituted for a fresh type variable `N`. This variable will
- eventually be resolved to some concrete type (which might itself be
- type parameter).
- */
- pub use self::Expectation::*;
- use self::coercion::{CoerceMany, DynamicCoerceMany};
- pub use self::compare_method::{compare_impl_method, compare_const_impl};
- use self::TupleArgumentsFlag::*;
- use astconv::AstConv;
- use fmt_macros::{Parser, Piece, Position};
- use hir::def::{Def, CtorKind};
- use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
- use rustc_back::slice::ref_slice;
- use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
- use rustc::infer::type_variable::{TypeVariableOrigin};
- use rustc::middle::region::CodeExtent;
- use rustc::ty::subst::{Kind, Subst, Substs};
- use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal};
- use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
- use rustc::ty::{self, Ty, TyCtxt, Visibility};
- use rustc::ty::{MethodCall, MethodCallee};
- use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
- use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
- use rustc::ty::maps::Providers;
- use rustc::ty::util::{Representability, IntTypeExt};
- use errors::DiagnosticBuilder;
- use require_c_abi_if_variadic;
- use session::{Session, CompileResult};
- use TypeAndSubsts;
- use lint;
- use util::common::{ErrorReported, indenter};
- use util::nodemap::{DefIdMap, FxHashMap, NodeMap};
- use std::cell::{Cell, RefCell};
- use std::collections::hash_map::Entry;
- use std::cmp;
- use std::mem::replace;
- use std::ops::{self, Deref};
- use syntax::abi::Abi;
- use syntax::ast;
- use syntax::codemap::{self, original_sp, Spanned};
- use syntax::feature_gate::{GateIssue, emit_feature_err};
- use syntax::ptr::P;
- use syntax::symbol::{Symbol, InternedString, keywords};
- use syntax::util::lev_distance::find_best_match_for_name;
- use syntax_pos::{self, BytePos, Span, DUMMY_SP};
- use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
- use rustc::hir::itemlikevisit::ItemLikeVisitor;
- use rustc::hir::{self, PatKind};
- use rustc::middle::lang_items;
- use rustc_back::slice;
- use rustc::middle::const_val::eval_length;
- use rustc_const_math::ConstInt;
- mod autoderef;
- pub mod dropck;
- pub mod _match;
- pub mod writeback;
- pub mod regionck;
- pub mod coercion;
- pub mod demand;
- pub mod method;
- mod upvar;
- mod wfcheck;
- mod cast;
- mod closure;
- mod callee;
- mod compare_method;
- mod intrinsic;
- mod op;
- /// closures defined within the function. For example:
- ///
- /// fn foo() {
- /// bar(move|| { ... })
- /// }
- ///
- /// Here, the function `foo()` and the closure passed to
- /// `bar()` will each have their own `FnCtxt`, but they will
- /// share the inherited fields.
- pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
- infcx: InferCtxt<'a, 'gcx, 'tcx>,
- locals: RefCell<NodeMap<Ty<'tcx>>>,
- fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,
- // When we process a call like `c()` where `c` is a closure type,
- // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
- // `FnOnce` closure. In that case, we defer full resolution of the
- // call until upvar inference can kick in and make the
- // decision. We keep these deferred resolutions grouped by the
- // def-id of the closure, so that once we decide, we can easily go
- // back and process them.
- deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'gcx, 'tcx>>>>,
- deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
- // Anonymized types found in explicit return types and their
- // associated fresh inference variable. Writeback resolves these
- // variables to get the concrete type, which can be used to
- // deanonymize TyAnon, after typeck is done with all functions.
- anon_types: RefCell<NodeMap<Ty<'tcx>>>,
- /// Each type parameter has an implicit region bound that
- /// indicates it must outlive at least the function body (the user
- /// may specify stronger requirements). This field indicates the
- /// region of the callee. If it is `None`, then the parameter
- /// environment is for an item or something where the "callee" is
- /// not clear.
- implicit_region_bound: Option<ty::Region<'tcx>>,
- }
- impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
- type Target = InferCtxt<'a, 'gcx, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.infcx
- }
- }
- trait DeferredCallResolution<'gcx, 'tcx> {
- fn resolve<'a>(&mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>);
- }
- type DeferredCallResolutionHandler<'gcx, 'tcx> = Box<DeferredCallResolution<'gcx, 'tcx>+'tcx>;
- /// When type-checking an expression, we propagate downward
- /// whatever type hint we are able in the form of an `Expectation`.
- #[derive(Copy, Clone, Debug)]
- pub enum Expectation<'tcx> {
- /// We know nothing about what type this expression should have.
- NoExpectation,
- /// This expression should have the type given (or some subtype)
- ExpectHasType(Ty<'tcx>),
- /// This expression will be cast to the `Ty`
- ExpectCastableToType(Ty<'tcx>),
- /// This rvalue expression will be wrapped in `&` or `Box` and coerced
- /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
- ExpectRvalueLikeUnsized(Ty<'tcx>),
- }
- impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
- // Disregard "castable to" expectations because they
- // can lead us astray. Consider for example `if cond
- // {22} else {c} as u8` -- if we propagate the
- // "castable to u8" constraint to 22, it will pick the
- // type 22u8, which is overly constrained (c might not
- // be a u8). In effect, the problem is that the
- // "castable to" expectation is not the tightest thing
- // we can say, so we want to drop it in this case.
- // The tightest thing we can say is "must unify with
- // else branch". Note that in the case of a "has type"
- // constraint, this limitation does not hold.
- // If the expected type is just a type variable, then don't use
- // an expected type. Otherwise, we might write parts of the type
- // when checking the 'then' block which are incompatible with the
- // 'else' branch.
- fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Expectation<'tcx> {
- match *self {
- ExpectHasType(ety) => {
- let ety = fcx.shallow_resolve(ety);
- if !ety.is_ty_var() {
- ExpectHasType(ety)
- } else {
- NoExpectation
- }
- }
- ExpectRvalueLikeUnsized(ety) => {
- ExpectRvalueLikeUnsized(ety)
- }
- _ => NoExpectation
- }
- }
- /// Provide an expectation for an rvalue expression given an *optional*
- /// hint, which is not required for type safety (the resulting type might
- /// be checked higher up, as is the case with `&expr` and `box expr`), but
- /// is useful in determining the concrete type.
- ///
- /// The primary use case is where the expected type is a fat pointer,
- /// like `&[isize]`. For example, consider the following statement:
- ///
- /// let x: &[isize] = &[1, 2, 3];
- ///
- /// In this case, the expected type for the `&[1, 2, 3]` expression is
- /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
- /// expectation `ExpectHasType([isize])`, that would be too strong --
- /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
- /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
- /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
- /// which still is useful, because it informs integer literals and the like.
- /// See the test case `test/run-pass/coerce-expect-unsized.rs` and #20169
- /// for examples of where this comes up,.
- fn rvalue_hint(fcx: &FnCtxt<'a, 'gcx, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
- match fcx.tcx.struct_tail(ty).sty {
- ty::TySlice(_) | ty::TyStr | ty::TyDynamic(..) => {
- ExpectRvalueLikeUnsized(ty)
- }
- _ => ExpectHasType(ty)
- }
- }
- // Resolves `expected` by a single level if it is a variable. If
- // there is no expected type or resolution is not possible (e.g.,
- // no constraints yet present), just returns `None`.
- fn resolve(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Expectation<'tcx> {
- match self {
- NoExpectation => {
- NoExpectation
- }
- ExpectCastableToType(t) => {
- ExpectCastableToType(fcx.resolve_type_vars_if_possible(&t))
- }
- ExpectHasType(t) => {
- ExpectHasType(fcx.resolve_type_vars_if_possible(&t))
- }
- ExpectRvalueLikeUnsized(t) => {
- ExpectRvalueLikeUnsized(fcx.resolve_type_vars_if_possible(&t))
- }
- }
- }
- fn to_option(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
- match self.resolve(fcx) {
- NoExpectation => None,
- ExpectCastableToType(ty) |
- ExpectHasType(ty) |
- ExpectRvalueLikeUnsized(ty) => Some(ty),
- }
- }
- /// It sometimes happens that we want to turn an expectation into
- /// a **hard constraint** (i.e., something that must be satisfied
- /// for the program to type-check). `only_has_type` will return
- /// such a constraint, if it exists.
- fn only_has_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
- match self.resolve(fcx) {
- ExpectHasType(ty) => Some(ty),
- _ => None
- }
- }
- /// Like `only_has_type`, but instead of returning `None` if no
- /// hard constraint exists, creates a fresh type variable.
- fn coercion_target_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> {
- self.only_has_type(fcx)
- .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)))
- }
- }
- #[derive(Copy, Clone)]
- pub struct UnsafetyState {
- pub def: ast::NodeId,
- pub unsafety: hir::Unsafety,
- pub unsafe_push_count: u32,
- from_fn: bool
- }
- impl UnsafetyState {
- pub fn function(unsafety: hir::Unsafety, def: ast::NodeId) -> UnsafetyState {
- UnsafetyState { def: def, unsafety: unsafety, unsafe_push_count: 0, from_fn: true }
- }
- pub fn recurse(&mut self, blk: &hir::Block) -> UnsafetyState {
- match self.unsafety {
- // If this unsafe, then if the outer function was already marked as
- // unsafe we shouldn't attribute the unsafe'ness to the block. This
- // way the block can be warned about instead of ignoring this
- // extraneous block (functions are never warned about).
- hir::Unsafety::Unsafe if self.from_fn => *self,
- unsafety => {
- let (unsafety, def, count) = match blk.rules {
- hir::PushUnsafeBlock(..) =>
- (unsafety, blk.id, self.unsafe_push_count.checked_add(1).unwrap()),
- hir::PopUnsafeBlock(..) =>
- (unsafety, blk.id, self.unsafe_push_count.checked_sub(1).unwrap()),
- hir::UnsafeBlock(..) =>
- (hir::Unsafety::Unsafe, blk.id, self.unsafe_push_count),
- hir::DefaultBlock =>
- (unsafety, self.def, self.unsafe_push_count),
- };
- UnsafetyState{ def: def,
- unsafety: unsafety,
- unsafe_push_count: count,
- from_fn: false }
- }
- }
- }
- }
- #[derive(Debug, Copy, Clone)]
- pub enum LvalueOp {
- Deref,
- Index
- }
- #[derive(Copy, Clone, Debug)]
- pub struct AdjustedRcvr<'a> {
- pub rcvr_expr: &'a hir::Expr,
- pub autoderefs: usize,
- pub unsize: bool
- }
- /// Tracks whether executing a node may exit normally (versus
- /// return/break/panic, which "diverge", leaving dead code in their
- /// wake). Tracked semi-automatically (through type variables marked
- /// as diverging), with some manual adjustments for control-flow
- /// primitives (approximating a CFG).
- #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
- pub enum Diverges {
- /// Potentially unknown, some cases converge,
- /// others require a CFG to determine them.
- Maybe,
- /// Definitely known to diverge and therefore
- /// not reach the next sibling or its parent.
- Always,
- /// Same as `Always` but with a reachability
- /// warning already emitted
- WarnedAlways
- }
- // Convenience impls for combinig `Diverges`.
- impl ops::BitAnd for Diverges {
- type Output = Self;
- fn bitand(self, other: Self) -> Self {
- cmp::min(self, other)
- }
- }
- impl ops::BitOr for Diverges {
- type Output = Self;
- fn bitor(self, other: Self) -> Self {
- cmp::max(self, other)
- }
- }
- impl ops::BitAndAssign for Diverges {
- fn bitand_assign(&mut self, other: Self) {
- *self = *self & other;
- }
- }
- impl ops::BitOrAssign for Diverges {
- fn bitor_assign(&mut self, other: Self) {
- *self = *self | other;
- }
- }
- impl Diverges {
- fn always(self) -> bool {
- self >= Diverges::Always
- }
- }
- pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> {
- may_break: bool,
- // this is `null` for loops where break with a value is illegal,
- // such as `while`, `for`, and `while let`
- coerce: Option<DynamicCoerceMany<'gcx, 'tcx>>,
- }
- pub struct EnclosingBreakables<'gcx: 'tcx, 'tcx> {
- stack: Vec<BreakableCtxt<'gcx, 'tcx>>,
- by_id: NodeMap<usize>,
- }
- impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
- fn find_breakable(&mut self, target_id: ast::NodeId) -> &mut BreakableCtxt<'gcx, 'tcx> {
- let ix = *self.by_id.get(&target_id).unwrap_or_else(|| {
- bug!("could not find enclosing breakable with id {}", target_id);
- });
- &mut self.stack[ix]
- }
- }
- pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
- body_id: ast::NodeId,
- // Number of errors that had been reported when we started
- // checking this function. On exit, if we find that *more* errors
- // have been reported, we will skip regionck and other work that
- // expects the types within the function to be consistent.
- err_count_on_creation: usize,
- ret_coercion: Option<RefCell<DynamicCoerceMany<'gcx, 'tcx>>>,
- ps: RefCell<UnsafetyState>,
- /// Whether the last checked node generates a divergence (e.g.,
- /// `return` will set this to Always). In general, when entering
- /// an expression or other node in the tree, the initial value
- /// indicates whether prior parts of the containing expression may
- /// have diverged. It is then typically set to `Maybe` (and the
- /// old value remembered) for processing the subparts of the
- /// current expression. As each subpart is processed, they may set
- /// the flag to `Always` etc. Finally, at the end, we take the
- /// result and "union" it with the original value, so that when we
- /// return the flag indicates if any subpart of the the parent
- /// expression (up to and including this part) has diverged. So,
- /// if you read it after evaluating a subexpression `X`, the value
- /// you get indicates whether any subexpression that was
- /// evaluating up to and including `X` diverged.
- ///
- /// We use this flag for two purposes:
- ///
- /// - To warn about unreachable code: if, after processing a
- /// sub-expression but before we have applied the effects of the
- /// current node, we see that the flag is set to `Always`, we
- /// can issue a warning. This corresponds to something like
- /// `foo(return)`; we warn on the `foo()` expression. (We then
- /// update the flag to `WarnedAlways` to suppress duplicate
- /// reports.) Similarly, if we traverse to a fresh statement (or
- /// tail expression) from a `Always` setting, we will isssue a
- /// warning. This corresponds to something like `{return;
- /// foo();}` or `{return; 22}`, where we would warn on the
- /// `foo()` or `22`.
- ///
- /// - To permit assignment into a local variable or other lvalue
- /// (including the "return slot") of type `!`. This is allowed
- /// if **either** the type of value being assigned is `!`, which
- /// means the current code is dead, **or** the expression's
- /// divering flag is true, which means that a divering value was
- /// wrapped (e.g., `let x: ! = foo(return)`).
- ///
- /// To repeat the last point: an expression represents dead-code
- /// if, after checking it, **either** its type is `!` OR the
- /// diverges flag is set to something other than `Maybe`.
- diverges: Cell<Diverges>,
- /// Whether any child nodes have any type errors.
- has_errors: Cell<bool>,
- enclosing_breakables: RefCell<EnclosingBreakables<'gcx, 'tcx>>,
- inh: &'a Inherited<'a, 'gcx, 'tcx>,
- }
- impl<'a, 'gcx, 'tcx> Deref for FnCtxt<'a, 'gcx, 'tcx> {
- type Target = Inherited<'a, 'gcx, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.inh
- }
- }
- /// Helper type of a temporary returned by Inherited::build(...).
- /// Necessary because we can't write the following bound:
- /// F: for<'b, 'tcx> where 'gcx: 'tcx FnOnce(Inherited<'b, 'gcx, 'tcx>).
- pub struct InheritedBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
- infcx: infer::InferCtxtBuilder<'a, 'gcx, 'tcx>,
- def_id: DefId,
- }
- impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
- pub fn build(tcx: TyCtxt<'a, 'gcx, 'gcx>, def_id: DefId)
- -> InheritedBuilder<'a, 'gcx, 'tcx> {
- let tables = ty::TypeckTables::empty();
- let param_env = tcx.parameter_environment(def_id);
- InheritedBuilder {
- infcx: tcx.infer_ctxt((tables, param_env), Reveal::UserFacing),
- def_id,
- }
- }
- }
- impl<'a, 'gcx, 'tcx> InheritedBuilder<'a, 'gcx, 'tcx> {
- fn enter<F, R>(&'tcx mut self, f: F) -> R
- where F: for<'b> FnOnce(Inherited<'b, 'gcx, 'tcx>) -> R
- {
- let def_id = self.def_id;
- self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
- }
- }
- impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
- fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> Self {
- let tcx = infcx.tcx;
- let item_id = tcx.hir.as_local_node_id(def_id);
- let body_id = item_id.and_then(|id| tcx.hir.maybe_body_owned_by(id));
- let implicit_region_bound = body_id.map(|body| {
- tcx.mk_region(ty::ReScope(CodeExtent::CallSiteScope(body)))
- });
- Inherited {
- infcx: infcx,
- fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
- locals: RefCell::new(NodeMap()),
- deferred_call_resolutions: RefCell::new(DefIdMap()),
- deferred_cast_checks: RefCell::new(Vec::new()),
- anon_types: RefCell::new(NodeMap()),
- implicit_region_bound,
- }
- }
- fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
- debug!("register_predicate({:?})", obligation);
- if obligation.has_escaping_regions() {
- span_bug!(obligation.cause.span, "escaping regions in predicate {:?}",
- obligation);
- }
- self.fulfillment_cx
- .borrow_mut()
- .register_predicate_obligation(self, obligation);
- }
- fn register_predicates(&self, obligations: Vec<traits::PredicateObligation<'tcx>>) {
- for obligation in obligations {
- self.register_predicate(obligation);
- }
- }
- fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
- self.register_predicates(infer_ok.obligations);
- infer_ok.value
- }
- fn normalize_associated_types_in<T>(&self,
- span: Span,
- body_id: ast::NodeId,
- value: &T) -> T
- where T : TypeFoldable<'tcx>
- {
- let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, value);
- self.register_infer_ok_obligations(ok)
- }
- fn normalize_associated_types_in_as_infer_ok<T>(&self,
- span: Span,
- body_id: ast::NodeId,
- value: &T)
- -> InferOk<'tcx, T>
- where T : TypeFoldable<'tcx>
- {
- debug!("normalize_associated_types_in(value={:?})", value);
- let mut selcx = traits::SelectionContext::new(self);
- let cause = ObligationCause::misc(span, body_id);
- let traits::Normalized { value, obligations } =
- traits::normalize(&mut selcx, cause, value);
- debug!("normalize_associated_types_in: result={:?} predicates={:?}",
- value,
- obligations);
- InferOk { value, obligations }
- }
- /// Replace any late-bound regions bound in `value` with
- /// free variants attached to `all_outlive_scope`.
- fn liberate_late_bound_regions<T>(&self,
- all_outlive_scope: DefId,
- value: &ty::Binder<T>)
- -> T
- where T: TypeFoldable<'tcx>
- {
- self.tcx.replace_late_bound_regions(value, |br| {
- self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
- scope: all_outlive_scope,
- bound_region: br
- }))
- }).0
- }
- }
- struct CheckItemTypesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
- impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
- fn visit_item(&mut self, i: &'tcx hir::Item) {
- check_item_type(self.tcx, i);
- }
- fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { }
- fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { }
- }
- pub fn check_wf_new<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
- tcx.sess.track_errors(|| {
- let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
- tcx.hir.krate().visit_all_item_likes(&mut visit.as_deep_visitor());
- })
- }
- pub fn check_item_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
- tcx.sess.track_errors(|| {
- tcx.hir.krate().visit_all_item_likes(&mut CheckItemTypesVisitor { tcx });
- })
- }
- pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
- tcx.typeck_item_bodies(LOCAL_CRATE)
- }
- fn typeck_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) -> CompileResult {
- debug_assert!(crate_num == LOCAL_CRATE);
- tcx.sess.track_errors(|| {
- for body_owner_def_id in tcx.body_owners() {
- tcx.typeck_tables_of(body_owner_def_id);
- }
- })
- }
- pub fn provide(providers: &mut Providers) {
- *providers = Providers {
- typeck_item_bodies,
- typeck_tables_of,
- has_typeck_tables,
- closure_type,
- closure_kind,
- adt_destructor,
- ..*providers
- };
- }
- fn closure_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId)
- -> ty::PolyFnSig<'tcx> {
- let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
- tcx.typeck_tables_of(def_id).closure_tys[&node_id]
- }
- fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId)
- -> ty::ClosureKind {
- let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
- tcx.typeck_tables_of(def_id).closure_kinds[&node_id]
- }
- fn adt_destructor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId)
- -> Option<ty::Destructor> {
- tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl)
- }
- /// If this def-id is a "primary tables entry", returns `Some((body_id, decl))`
- /// with information about it's body-id and fn-decl (if any). Otherwise,
- /// returns `None`.
- ///
- /// If this function returns "some", then `typeck_tables(def_id)` will
- /// succeed; if it returns `None`, then `typeck_tables(def_id)` may or
- /// may not succeed. In some cases where this function returns `None`
- /// (notably closures), `typeck_tables(def_id)` would wind up
- /// redirecting to the owning function.
- fn primary_body_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- id: ast::NodeId)
- -> Option<(hir::BodyId, Option<&'tcx hir::FnDecl>)>
- {
- match tcx.hir.get(id) {
- hir::map::NodeItem(item) => {
- match item.node {
- hir::ItemConst(_, body) |
- hir::ItemStatic(_, _, body) =>
- Some((body, None)),
- hir::ItemFn(ref decl, .., body) =>
- Some((body, Some(decl))),
- _ =>
- None,
- }
- }
- hir::map::NodeTraitItem(item) => {
- match item.node {
- hir::TraitItemKind::Const(_, Some(body)) =>
- Some((body, None)),
- hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) =>
- Some((body, Some(&sig.decl))),
- _ =>
- None,
- }
- }
- hir::map::NodeImplItem(item) => {
- match item.node {
- hir::ImplItemKind::Const(_, body) =>
- Some((body, None)),
- hir::ImplItemKind::Method(ref sig, body) =>
- Some((body, Some(&sig.decl))),
- _ =>
- None,
- }
- }
- hir::map::NodeExpr(expr) => {
- // FIXME(eddyb) Closures should have separate
- // function definition IDs and expression IDs.
- // Type-checking should not let closures get
- // this far in a constant position.
- // Assume that everything other than closures
- // is a constant "initializer" expression.
- match expr.node {
- hir::ExprClosure(..) =>
- None,
- _ =>
- Some((hir::BodyId { node_id: expr.id }, None)),
- }
- }
- _ => None,
- }
- }
- fn has_typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId)
- -> bool {
- // Closures' tables come from their outermost function,
- // as they are part of the same "inference environment".
- let outer_def_id = tcx.closure_base_def_id(def_id);
- if outer_def_id != def_id {
- return tcx.has_typeck_tables(outer_def_id);
- }
- let id = tcx.hir.as_local_node_id(def_id).unwrap();
- primary_body_of(tcx, id).is_some()
- }
- fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId)
- -> &'tcx ty::TypeckTables<'tcx> {
- // Closures' tables come from their outermost function,
- // as they are part of the same "inference environment".
- let outer_def_id = tcx.closure_base_def_id(def_id);
- if outer_def_id != def_id {
- return tcx.typeck_tables_of(outer_def_id);
- }
- let id = tcx.hir.as_local_node_id(def_id).unwrap();
- let span = tcx.hir.span(id);
- // Figure out what primary body this item has.
- let (body_id, fn_decl) = primary_body_of(tcx, id).unwrap_or_else(|| {
- span_bug!(span, "can't type-check body of {:?}", def_id);
- });
- let body = tcx.hir.body(body_id);
- Inherited::build(tcx, def_id).enter(|inh| {
- let fcx = if let Some(decl) = fn_decl {
- let fn_sig = tcx.type_of(def_id).fn_sig();
- check_abi(tcx, span, fn_sig.abi());
- // Compute the fty from point of view of inside fn.
- let fn_sig =
- inh.liberate_late_bound_regions(def_id, &fn_sig);
- let fn_sig =
- inh.normalize_associated_types_in(body.value.span, body_id.node_id, &fn_sig);
- check_fn(&inh, fn_sig, decl, id, body)
- } else {
- let fcx = FnCtxt::new(&inh, body.value.id);
- let expected_type = tcx.type_of(def_id);
- let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
- fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
- // Gather locals in statics (because of block expressions).
- // This is technically unnecessary because locals in static items are forbidden,
- // but prevents type checking from blowing up before const checking can properly
- // emit an error.
- GatherLocalsVisitor { fcx: &fcx }.visit_body(body);
- fcx.check_expr_coercable_to_type(&body.value, expected_type);
- fcx
- };
- fcx.select_all_obligations_and_apply_defaults();
- fcx.closure_analyze(body);
- fcx.select_obligations_where_possible();
- fcx.check_casts();
- fcx.select_all_obligations_or_error();
- if fn_decl.is_some() {
- fcx.regionck_fn(id, body);
- } else {
- fcx.regionck_expr(body);
- }
- fcx.resolve_type_vars_in_body(body)
- })
- }
- fn check_abi<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span: Span, abi: Abi) {
- if !tcx.sess.target.target.is_abi_supported(abi) {
- struct_span_err!(tcx.sess, span, E0570,
- "The ABI `{}` is not supported for the current target", abi).emit()
- }
- }
- struct GatherLocalsVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
- fcx: &'a FnCtxt<'a, 'gcx, 'tcx>
- }
- impl<'a, 'gcx, 'tcx> GatherLocalsVisitor<'a, 'gcx, 'tcx> {
- fn assign(&mut self, span: Span, nid: ast::NodeId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
- match ty_opt {
- None => {
- // infer the variable's type
- let var_ty = self.fcx.next_ty_var(TypeVariableOrigin::TypeInference(span));
- self.fcx.locals.borrow_mut().insert(nid, var_ty);
- var_ty
- }
- Some(typ) => {
- // take type that the user specified
- self.fcx.locals.borrow_mut().insert(nid, typ);
- typ
- }
- }
- }
- }
- impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
- NestedVisitorMap::None
- }
- // Add explicitly-declared locals.
- fn visit_local(&mut self, local: &'gcx hir::Local) {
- let o_ty = match local.ty {
- Some(ref ty) => Some(self.fcx.to_ty(&ty)),
- None => None
- };
- self.assign(local.span, local.id, o_ty);
- debug!("Local variable {:?} is assigned type {}",
- local.pat,
- self.fcx.ty_to_string(
- self.fcx.locals.borrow().get(&local.id).unwrap().clone()));
- intravisit::walk_local(self, local);
- }
- // Add pattern bindings.
- fn visit_pat(&mut self, p: &'gcx hir::Pat) {
- if let PatKind::Binding(_, _, ref path1, _) = p.node {
- let var_ty = self.assign(p.span, p.id, None);
- self.fcx.require_type_is_sized(var_ty, p.span,
- traits::VariableType(p.id));
- debug!("Pattern binding {} is assigned to {} with type {:?}",
- path1.node,
- self.fcx.ty_to_string(
- self.fcx.locals.borrow().get(&p.id).unwrap().clone()),
- var_ty);
- }
- intravisit::walk_pat(self, p);
- }
- // Don't descend into the bodies of nested closures
- fn visit_fn(&mut self, _: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl,
- _: hir::BodyId, _: Span, _: ast::NodeId) { }
- }
- /// Helper used for fns and closures. Does the grungy work of checking a function
- /// body and returns the function context used for that purpose, since in the case of a fn item
- /// there is still a bit more to do.
- ///
- /// * ...
- /// * inherited: other fields inherited from the enclosing fn (if any)
- fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
- fn_sig: ty::FnSig<'tcx>,
- decl: &'gcx hir::FnDecl,
- fn_id: ast::NodeId,
- body: &'gcx hir::Body)
- -> FnCtxt<'a, 'gcx, 'tcx>
- {
- let mut fn_sig = fn_sig.clone();
- debug!("check_fn(sig={:?}, fn_id={})", fn_sig, fn_id);
- // Create the function context. This is either derived from scratch or,
- // in the case of function expressions, based on the outer context.
- let mut fcx = FnCtxt::new(inherited, body.value.id);
- *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
- let ret_ty = fn_sig.output();
- fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
- let ret_ty = fcx.instantiate_anon_types(&ret_ty);
- fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
- fn_sig = fcx.tcx.mk_fn_sig(
- fn_sig.inputs().iter().cloned(),
- ret_ty,
- fn_sig.variadic,
- fn_sig.unsafety,
- fn_sig.abi
- );
- GatherLocalsVisitor { fcx: &fcx, }.visit_body(body);
- // Add formal parameters.
- for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) {
- // The type of the argument must be well-formed.
- //
- // NB -- this is now checked in wfcheck, but that
- // currently only results in warnings, so we issue an
- // old-style WF obligation here so that we still get the
- // errors that we used to get.
- fcx.register_old_wf_obligation(arg_ty, arg.pat.span, traits::MiscObligation);
- // Check the pattern.
- fcx.check_pat_arg(&arg.pat, arg_ty, true);
- fcx.write_ty(arg.id, arg_ty);
- }
- inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
- fcx.check_return_expr(&body.value);
- // Finalize the return check by taking the LUB of the return types
- // we saw and assigning it to the expected return type. This isn't
- // really expected to fail, since the coercions would have failed
- // earlier when trying to find a LUB.
- //
- // However, the behavior around `!` is sort of complex. In the
- // event that the `actual_return_ty` comes back as `!`, that
- // indicates that the fn either does not return or "returns" only
- // values of type `!`. In this case, if there is an expected
- // return type that is *not* `!`, that should be ok. But if the
- // return type is being inferred, we want to "fallback" to `!`:
- //
- // let x = move || panic!();
- //
- // To allow for that, I am creating a type variable with diverging
- // fallback. This was deemed ever so slightly better than unifying
- // the return value with `!` because it allows for the caller to
- // make more assumptions about the return type (e.g., they could do
- //
- // let y: Option<u32> = Some(x());
- //
- // which would then cause this return type to become `u32`, not
- // `!`).
- let coercion = fcx.ret_coercion.take().unwrap().into_inner();
- let mut actual_return_ty = coercion.complete(&fcx);
- if actual_return_ty.is_never() {
- actual_return_ty = fcx.next_diverging_ty_var(
- TypeVariableOrigin::DivergingFn(body.value.span));
- }
- fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty);
- fcx
- }
- fn check_struct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- id: ast::NodeId,
- span: Span) {
- let def_id = tcx.hir.local_def_id(id);
- let def = tcx.adt_def(def_id);
- def.destructor(tcx); // force the destructor to be evaluated
- check_representable(tcx, span, def_id);
- if def.repr.simd() {
- check_simd(tcx, span, def_id);
- }
- // if struct is packed and not aligned, check fields for alignment.
- // Checks for combining packed and align attrs on single struct are done elsewhere.
- if tcx.adt_def(def_id).repr.packed() && tcx.adt_def(def_id).repr.align == 0 {
- check_packed(tcx, span, def_id);
- }
- }
- fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- id: ast::NodeId,
- span: Span) {
- let def_id = tcx.hir.local_def_id(id);
- let def = tcx.adt_def(def_id);
- def.destructor(tcx); // force the destructor to be evaluated
- check_representable(tcx, span, def_id);
- }
- pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item) {
- debug!("check_item_type(it.id={}, it.name={})",
- it.id,
- tcx.item_path_str(tcx.hir.local_def_id(it.id)));
- let _indenter = indenter();
- match it.node {
- // Consts can play a role in type-checking, so they are included here.
- hir::ItemStatic(..) |
- hir::ItemConst(..) => {
- tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
- }
- hir::ItemEnum(ref enum_definition, _) => {
- check_enum(tcx,
- it.span,
- &enum_definition.variants,
- it.id);
- }
- hir::ItemFn(..) => {} // entirely within check_item_body
- hir::ItemImpl(.., ref impl_item_refs) => {
- debug!("ItemImpl {} with id {}", it.name, it.id);
- let impl_def_id = tcx.hir.local_def_id(it.id);
- if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) {
- check_impl_items_against_trait(tcx,
- it.span,
- impl_def_id,
- impl_trait_ref,
- impl_item_refs);
- let trait_def_id = impl_trait_ref.def_id;
- check_on_unimplemented(tcx, trait_def_id, it);
- }
- }
- hir::ItemTrait(..) => {
- let def_id = tcx.hir.local_def_id(it.id);
- check_on_unimplemented(tcx, def_id, it);
- }
- hir::ItemStruct(..) => {
- check_struct(tcx, it.id, it.span);
- }
- hir::ItemUnion(..) => {
- check_union(tcx, it.id, it.span);
- }
- hir::ItemTy(_, ref generics) => {
- let def_id = tcx.hir.local_def_id(it.id);
- let pty_ty = tcx.type_of(def_id);
- check_bounds_are_used(tcx, generics, pty_ty);
- }
- hir::ItemForeignMod(ref m) => {
- check_abi(tcx, it.span, m.abi);
- if m.abi == Abi::RustIntrinsic {
- for item in &m.items {
- intrinsic::check_intrinsic_type(tcx, item);
- }
- } else if m.abi == Abi::PlatformIntrinsic {
- for item in &m.items {
- intrinsic::check_platform_intrinsic_type(tcx, item);
- }
- } else {
- for item in &m.items {
- let generics = tcx.generics_of(tcx.hir.local_def_id(item.id));
- if !generics.types.is_empty() {
- let mut err = struct_span_err!(tcx.sess, item.span, E0044,
- "foreign items may not have type parameters");
- span_help!(&mut err, item.span,
- "consider using specialization instead of \
- type parameters");
- err.emit();
- }
- if let hir::ForeignItemFn(ref fn_decl, _, _) = item.node {
- require_c_abi_if_variadic(tcx, fn_decl, m.abi, item.span);
- }
- }
- }
- }
- _ => {/* nothing to do */ }
- }
- }
- fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- def_id: DefId,
- item: &hir::Item) {
- let generics = tcx.generics_of(def_id);
- if let Some(ref attr) = item.attrs.iter().find(|a| {
- a.check_name("rustc_on_unimplemented")
- }) {
- if let Some(istring) = attr.value_str() {
- let istring = istring.as_str();
- let parser = Parser::new(&istring);
- let types = &generics.types;
- for token in parser {
- match token {
- Piece::String(_) => (), // Normal string, no need to check it
- Piece::NextArgument(a) => match a.position {
- // `{Self}` is allowed
- Position::ArgumentNamed(s) if s == "Self" => (),
- // So is `{A}` if A is a type parameter
- Position::ArgumentNamed(s) => match types.iter().find(|t| {
- t.name == s
- }) {
- Some(_) => (),
- None => {
- let name = tcx.item_name(def_id);
- span_err!(tcx.sess, attr.span, E0230,
- "there is no type parameter \
- {} on trait {}",
- s, name);
- }
- },
- // `{:1}` and `{}` are not to be used
- Position::ArgumentIs(_) => {
- span_err!(tcx.sess, attr.span, E0231,
- "only named substitution \
- parameters are allowed");
- }
- }
- }
- }
- } else {
- struct_span_err!(
- tcx.sess, attr.span, E0232,
- "this attribute must have a value")
- .span_label(attr.span, "attribute requires a value")
- .note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`"))
- .emit();
- }
- }
- }
- fn report_forbidden_specialization<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- impl_item: &hir::ImplItem,
- parent_impl: DefId)
- {
- let mut err = struct_span_err!(
- tcx.sess, impl_item.span, E0520,
- "`{}` specializes an item from a parent `impl`, but \
- that item is not marked `default`",
- impl_item.name);
- err.span_label(impl_item.span, format!("cannot specialize default item `{}`",
- impl_item.name));
- match tcx.span_of_impl(parent_impl) {
- Ok(span) => {
- err.span_label(span, "parent `impl` is here");
- err.note(&format!("to specialize, `{}` in the parent `impl` must be marked `default`",
- impl_item.name));
- }
- Err(cname) => {
- err.note(&format!("parent implementation is in crate `{}`", cname));
- }
- }
- err.emit();
- }
- fn check_specialization_validity<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- trait_def: &ty::TraitDef,
- impl_id: DefId,
- impl_item: &hir::ImplItem)
- {
- let ancestors = trait_def.ancestors(tcx, impl_id);
- let kind = match impl_item.node {
- hir::ImplItemKind::Const(..) => ty::AssociatedKind::Const,
- hir::ImplItemKind::Method(..) => ty::AssociatedKind::Method,
- hir::ImplItemKind::Type(_) => ty::AssociatedKind::Type
- };
- let parent = ancestors.defs(tcx, impl_item.name, kind).skip(1).next()
- .map(|node_item| node_item.map(|parent| parent.defaultness));
- if let Some(parent) = parent {
- if tcx.impl_item_is_final(&parent) {
- report_forbidden_specialization(tcx, impl_item, parent.node.def_id());
- }
- }
- }
- fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- impl_span: Span,
- impl_id: DefId,
- impl_trait_ref: ty::TraitRef<'tcx>,
- impl_item_refs: &[hir::ImplItemRef]) {
- // If the trait reference itself is erroneous (so the compilation is going
- // to fail), skip checking the items here -- the `impl_item` table in `tcx`
- // isn't populated for such impls.
- if impl_trait_ref.references_error() { return; }
- // Locate trait definition and items
- let trait_def = tcx.trait_def(impl_trait_ref.def_id);
- let mut overridden_associated_type = None;
- let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir.impl_item(iiref.id));
- // Check existing impl methods to see if they are both present in trait
- // and compatible with trait signature
- for impl_item in impl_items() {
- let ty_impl_item = tcx.associated_item(tcx.hir.local_def_id(impl_item.id));
- let ty_trait_item = tcx.associated_items(impl_trait_ref.def_id)
- .find(|ac| ac.name == ty_impl_item.name);
- // Check that impl definition matches trait definition
- if let Some(ty_trait_item) = ty_trait_item {
- match impl_item.node {
- hir::ImplItemKind::Const(..) => {
- // Find associated const definition.
- if ty_trait_item.kind == ty::AssociatedKind::Const {
- compare_const_impl(tcx,
- &ty_impl_item,
- impl_item.span,
- &ty_trait_item,
- impl_trait_ref);
- } else {
- let mut err = struct_span_err!(tcx.sess, impl_item.span, E0323,
- "item `{}` is an associated const, \
- which doesn't match its trait `{}`",
- ty_impl_item.name,
- impl_trait_ref);
- err.span_label(impl_item.span, "does not match trait");
- // We can only get the spans from local trait definition
- // Same for E0324 and E0325
- if let Some(trait_span) = tcx.hir.span_if_local(ty_trait_item.def_id) {
- err.span_label(trait_span, "item in trait");
- }
- err.emit()
- }
- }
- hir::ImplItemKind::Method(..) => {
- let trait_span = tcx.hir.span_if_local(ty_trait_item.def_id);
- if ty_trait_item.kind == ty::AssociatedKind::Method {
- let err_count = tcx.sess.err_count();
- compare_impl_method(tcx,
- &ty_impl_item,
- impl_item.span,
- &ty_trai…
Large files files are truncated, but you can click here to view the full file