X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc_typeck%2Fcheck%2Fmod.rs;h=be008d522858ce35591be59fd60d0b0a9a2fe335;hb=6d992728c331128810704d16d8026aa4fa2e0c87;hp=6221134afd38a490b737f0deecbf0f95f9df6c7d;hpb=021389f6adbb215bb8fe267c93bc1a9daeb2ec14;p=rust.git diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6221134afd3..be008d52285 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,7 +90,7 @@ use middle::infer::type_variable; use middle::pat_util::{self, pat_id_map}; use middle::privacy::{AllPublic, LastMod}; -use middle::region::{self, CodeExtent}; +use middle::region::{self}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace}; use middle::traits::{self, report_fulfillment_errors}; use middle::ty::{FnSig, GenericPredicates, TypeScheme}; @@ -133,7 +133,8 @@ pub mod demand; pub mod method; mod upvar; -pub mod wf; +mod wf; +mod wfcheck; mod cast; mod closure; mod callee; @@ -382,7 +383,12 @@ fn visit_item(&mut self, i: &'tcx ast::Item) { } } -pub fn check_item_types(ccx: &CrateCtxt) { +pub fn check_wf_old(ccx: &CrateCtxt) { + // FIXME(#25759). The new code below is much more reliable but (for now) + // only generates warnings. So as to ensure that we continue + // getting errors where we used to get errors, we run the old wf + // code first and abort if it encounters any errors. If no abort + // comes, we run the new code and issue warnings. let krate = ccx.tcx.map.krate(); let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx); visit::walk_crate(&mut visit, krate); @@ -390,17 +396,34 @@ pub fn check_item_types(ccx: &CrateCtxt) { // If types are not well-formed, it leads to all manner of errors // downstream, so stop reporting errors at this point. ccx.tcx.sess.abort_if_errors(); +} - let mut visit = CheckItemTypesVisitor { ccx: ccx }; +pub fn check_wf_new(ccx: &CrateCtxt) { + let krate = ccx.tcx.map.krate(); + let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx); visit::walk_crate(&mut visit, krate); + // If types are not well-formed, it leads to all manner of errors + // downstream, so stop reporting errors at this point. + ccx.tcx.sess.abort_if_errors(); +} + +pub fn check_item_types(ccx: &CrateCtxt) { + let krate = ccx.tcx.map.krate(); + let mut visit = CheckItemTypesVisitor { ccx: ccx }; + visit::walk_crate(&mut visit, krate); ccx.tcx.sess.abort_if_errors(); +} +pub fn check_item_bodies(ccx: &CrateCtxt) { + let krate = ccx.tcx.map.krate(); let mut visit = CheckItemBodiesVisitor { ccx: ccx }; visit::walk_crate(&mut visit, krate); ccx.tcx.sess.abort_if_errors(); +} +pub fn check_drop_impls(ccx: &CrateCtxt) { for drop_method_did in ccx.tcx.destructors.borrow().iter() { if drop_method_did.krate == ast::LOCAL_CRATE { let drop_impl_did = ccx.tcx.map.get_parent_did(drop_method_did.node); @@ -445,9 +468,8 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fcx.select_all_obligations_and_apply_defaults(); upvar::closure_analyze_fn(&fcx, fn_id, decl, body); - fcx.select_all_obligations_or_error(); + fcx.select_obligations_where_possible(); fcx.check_casts(); - fcx.select_all_obligations_or_error(); // Casts can introduce new obligations. regionck::regionck_fn(&fcx, fn_id, fn_span, decl, body); @@ -587,7 +609,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, if let ty::FnConverging(ret_ty) = ret_ty { fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType); - fn_sig_tys.push(ret_ty); + fn_sig_tys.push(ret_ty); // FIXME(#25759) just take implied bounds from the arguments } debug!("fn-sig-map: fn_id={} fn_sig_tys={:?}", @@ -601,6 +623,14 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, // Add formal parameters. for (arg_ty, input) in arg_tys.iter().zip(&decl.inputs) { + // 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, input.ty.span, traits::MiscObligation); + // Create type variables for each argument. pat_util::pat_bindings( &tcx.def_map, @@ -1379,68 +1409,68 @@ fn normalize_associated_type(&self, cause) } - /// Returns the type of `def_id` with all generics replaced by by fresh type/region variables. - /// Also returns the substitution from the type parameters on `def_id` to the fresh variables. - /// Registers any trait obligations specified on `def_id` at the same time. + /// Instantiates the type in `did` with the generics in `path` and returns + /// it (registering the necessary trait obligations along the way). /// - /// Note that function is only intended to be used with types (notably, not fns). This is - /// because it doesn't do any instantiation of late-bound regions. + /// Note that this function is only intended to be used with type-paths, + /// not with value-paths. pub fn instantiate_type(&self, - span: Span, - def_id: ast::DefId) - -> TypeAndSubsts<'tcx> + did: ast::DefId, + path: &ast::Path) + -> Ty<'tcx> { + debug!("instantiate_type(did={:?}, path={:?})", did, path); let type_scheme = - self.tcx().lookup_item_type(def_id); + self.tcx().lookup_item_type(did); let type_predicates = - self.tcx().lookup_predicates(def_id); - let substs = - self.infcx().fresh_substs_for_generics( - span, - &type_scheme.generics); + self.tcx().lookup_predicates(did); + let substs = astconv::ast_path_substs_for_ty(self, self, + path.span, + PathParamMode::Optional, + &type_scheme.generics, + path.segments.last().unwrap()); + debug!("instantiate_type: ty={:?} substs={:?}", &type_scheme.ty, &substs); let bounds = - self.instantiate_bounds(span, &substs, &type_predicates); + self.instantiate_bounds(path.span, &substs, &type_predicates); self.add_obligations_for_parameters( traits::ObligationCause::new( - span, + path.span, self.body_id, - traits::ItemObligation(def_id)), + traits::ItemObligation(did)), &bounds); - let monotype = - self.instantiate_type_scheme(span, &substs, &type_scheme.ty); - TypeAndSubsts { - ty: monotype, - substs: substs - } + self.instantiate_type_scheme(path.span, &substs, &type_scheme.ty) } - /// Returns the type that this AST path refers to. If the path has no type - /// parameters and the corresponding type has type parameters, fresh type - /// and/or region variables are substituted. - /// - /// This is used when checking the constructor in struct literals. - fn instantiate_struct_literal_ty(&self, - did: ast::DefId, - path: &ast::Path) - -> TypeAndSubsts<'tcx> + /// Return the dict-like variant corresponding to a given `Def`. + pub fn def_struct_variant(&self, + def: def::Def) + -> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)> { - let tcx = self.tcx(); - - let ty::TypeScheme { generics, ty: decl_ty } = - tcx.lookup_item_type(did); - - let substs = astconv::ast_path_substs_for_ty(self, self, - path.span, - PathParamMode::Optional, - &generics, - path.segments.last().unwrap()); - - let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty); + let (adt, variant) = match def { + def::DefVariant(enum_id, variant_id, true) => { + let adt = self.tcx().lookup_adt_def(enum_id); + (adt, adt.variant_with_id(variant_id)) + } + def::DefTy(did, _) | def::DefStruct(did) => { + let typ = self.tcx().lookup_item_type(did); + if let ty::TyStruct(adt, _) = typ.ty.sty { + (adt, adt.struct_variant()) + } else { + return None; + } + } + _ => return None + }; - TypeAndSubsts { substs: substs, ty: ty } + if let ty::VariantKind::Dict = variant.kind() { + Some((adt, variant)) + } else { + None + } } + pub fn write_nil(&self, node_id: ast::NodeId) { self.write_ty(node_id, self.tcx().mk_nil()); } @@ -1508,10 +1538,19 @@ pub fn register_predicate(&self, pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> { let t = ast_ty_to_ty(self, self, ast_t); - let mut bounds_checker = wf::BoundsChecker::new(self, - self.body_id, - None); - bounds_checker.check_ty(t, ast_t.span); + // Generally speaking, we must check that types entered by the + // user are well-formed. This is not true for `_`, since those + // types are generated by inference. Now, you might think that + // we could as well generate a WF obligation -- but + // unfortunately that breaks code like `foo as *const _`, + // because those type variables wind up being unconstrained + // until very late. Nasty. Probably it'd be best to refactor + // that code path, but that's tricky because of + // defaults. Argh! + match ast_t.node { + ast::TyInfer => { } + _ => { self.register_wf_obligation(t, ast_t.span, traits::MiscObligation); } + } t } @@ -1630,15 +1669,38 @@ pub fn register_region_obligation(&self, fulfillment_cx.register_region_obligation(ty, region, cause); } - pub fn add_default_region_param_bounds(&self, - substs: &Substs<'tcx>, - expr: &ast::Expr) + /// Registers an obligation for checking later, during regionck, that the type `ty` must + /// outlive the region `r`. + pub fn register_wf_obligation(&self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>) + { + // WF obligations never themselves fail, so no real need to give a detailed cause: + let cause = traits::ObligationCause::new(span, self.body_id, code); + self.register_predicate(traits::Obligation::new(cause, ty::Predicate::WellFormed(ty))); + } + + pub fn register_old_wf_obligation(&self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>) + { + // Registers an "old-style" WF obligation that uses the + // implicator code. This is basically a buggy version of + // `register_wf_obligation` that is being kept around + // temporarily just to help with phasing in the newer rules. + // + // FIXME(#27579) all uses of this should be migrated to register_wf_obligation eventually + let cause = traits::ObligationCause::new(span, self.body_id, code); + self.register_region_obligation(ty, ty::ReEmpty, cause); + } + + /// Registers obligations that all types appearing in `substs` are well-formed. + pub fn add_wf_bounds(&self, substs: &Substs<'tcx>, expr: &ast::Expr) { for &ty in &substs.types { - let default_bound = ty::ReScope(CodeExtent::from_node_id(expr.id)); - let cause = traits::ObligationCause::new(expr.span, self.body_id, - traits::MiscObligation); - self.register_region_obligation(ty, default_bound, cause); + self.register_wf_obligation(ty, expr.span, traits::MiscObligation); } } @@ -2370,6 +2432,12 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, 1 }; + // All the input types from the fn signature must outlive the call + // so as to validate implied bounds. + for &fn_input_ty in fn_inputs { + fcx.register_wf_obligation(fn_input_ty, sp, traits::MiscObligation); + } + let mut expected_arg_tys = expected_arg_tys; let expected_arg_count = fn_inputs.len(); let formal_tys = if tuple_arguments == TupleArguments { @@ -2478,7 +2546,8 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Expectation::rvalue_hint(fcx.tcx(), ty) }); - check_expr_with_unifier(fcx, &**arg, + check_expr_with_unifier(fcx, + &**arg, expected.unwrap_or(ExpectHasType(formal_ty)), NoPreference, || { // 2. Coerce to the most detailed type that could be coerced @@ -3031,18 +3100,17 @@ fn report_unknown_field<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } - fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - adt_ty: Ty<'tcx>, - span: Span, - variant_id: ast::DefId, - ast_fields: &'tcx [ast::Field], - check_completeness: bool) -> Result<(),()> { + fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + adt_ty: Ty<'tcx>, + span: Span, + variant: ty::VariantDef<'tcx>, + ast_fields: &'tcx [ast::Field], + check_completeness: bool) { let tcx = fcx.ccx.tcx; - let (adt_def, substs) = match adt_ty.sty { - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => (def, substs), - _ => tcx.sess.span_bug(span, "non-ADT passed to check_struct_or_variant_fields") + let substs = match adt_ty.sty { + ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs, + _ => tcx.sess.span_bug(span, "non-ADT passed to check_expr_struct_fields") }; - let variant = adt_def.variant_with_id(variant_id); let mut remaining_fields = FnvHashMap(); for field in &variant.fields { @@ -3079,7 +3147,6 @@ fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, !error_happened && !remaining_fields.is_empty() { - error_happened = true; span_err!(tcx.sess, span, E0063, "missing field{}: {}", if remaining_fields.len() == 1 {""} else {"s"}, @@ -3088,68 +3155,6 @@ fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, .collect::>() .join(", ")); } - - if error_happened { Err(()) } else { Ok(()) } - } - - fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - id: ast::NodeId, - span: codemap::Span, - struct_def: ty::AdtDef<'tcx>, - fields: &'tcx [ast::Field], - base_expr: Option<&'tcx ast::Expr>) { - let tcx = fcx.ccx.tcx; - - // Generate the struct type. - let TypeAndSubsts { - ty: mut struct_type, - substs: _ - } = fcx.instantiate_type(span, struct_def.did); - - // Look up and check the fields. - let res = check_struct_or_variant_fields(fcx, - struct_type, - span, - struct_def.did, - fields, - base_expr.is_none()); - if res.is_err() { - struct_type = tcx.types.err; - } - - // Check the base expression if necessary. - match base_expr { - None => {} - Some(base_expr) => { - check_expr_has_type(fcx, &*base_expr, struct_type); - } - } - - // Write in the resulting type. - fcx.write_ty(id, struct_type); - } - - fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - id: ast::NodeId, - span: codemap::Span, - enum_id: ast::DefId, - variant_id: ast::DefId, - fields: &'tcx [ast::Field]) { - // Look up the number of type parameters and the raw type, and - // determine whether the enum is region-parameterized. - let TypeAndSubsts { - ty: enum_type, - substs: _ - } = fcx.instantiate_type(span, enum_id); - - // Look up and check the enum variant fields. - let _ = check_struct_or_variant_fields(fcx, - enum_type, - span, - variant_id, - fields, - true); - fcx.write_ty(id, enum_type); } fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, @@ -3168,6 +3173,42 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, } } + fn check_expr_struct<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + path: &ast::Path, + fields: &'tcx [ast::Field], + base_expr: &'tcx Option>) + { + let tcx = fcx.tcx(); + + // Find the relevant variant + let def = lookup_full_def(tcx, path.span, expr.id); + let (adt, variant) = match fcx.def_struct_variant(def) { + Some((adt, variant)) => (adt, variant), + None => { + span_err!(fcx.tcx().sess, path.span, E0071, + "`{}` does not name a structure", + pprust::path_to_string(path)); + check_struct_fields_on_error(fcx, expr.id, fields, base_expr); + return; + } + }; + + let expr_ty = fcx.instantiate_type(def.def_id(), path); + fcx.write_ty(expr.id, expr_ty); + + check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields, + base_expr.is_none()); + + if let &Some(ref base_expr) = base_expr { + check_expr_has_type(fcx, base_expr, expr_ty); + if adt.adt_kind() == ty::AdtKind::Enum { + span_err!(tcx.sess, base_expr.span, E0436, + "functional record update syntax requires a struct"); + } + } + } + type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty); let tcx = fcx.ccx.tcx; @@ -3363,7 +3404,9 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, // We always require that the type provided as the value for // a type parameter outlives the moment of instantiation. - constrain_path_type_parameters(fcx, expr); + fcx.opt_node_ty_substs(expr.id, |item_substs| { + fcx.add_wf_bounds(&item_substs.substs, expr); + }); } ast::ExprInlineAsm(ref ia) => { for &(_, ref input) in &ia.inputs { @@ -3476,16 +3519,18 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, } ast::ExprCall(ref callee, ref args) => { callee::check_call(fcx, expr, &**callee, &args[..], expected); + + // we must check that return type of called functions is WF: + let ret_ty = fcx.expr_ty(expr); + fcx.register_wf_obligation(ret_ty, expr.span, traits::MiscObligation); } ast::ExprMethodCall(ident, ref tps, ref args) => { - check_method_call(fcx, expr, ident, &args[..], &tps[..], expected, lvalue_pref); - let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a)); - let args_err = arg_tys.fold(false, - |rest_err, a| { - rest_err || a.references_error()}); - if args_err { - fcx.write_error(id); - } + check_method_call(fcx, expr, ident, &args[..], &tps[..], expected, lvalue_pref); + let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a)); + let args_err = arg_tys.fold(false, |rest_err, a| rest_err || a.references_error()); + if args_err { + fcx.write_error(id); + } } ast::ExprCast(ref e, ref t) => { if let ast::TyFixedLengthVec(_, ref count_expr) = t.node { @@ -3616,83 +3661,7 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, } } ast::ExprStruct(ref path, ref fields, ref base_expr) => { - // Resolve the path. - let def = lookup_full_def(tcx, path.span, id); - let struct_id = match def { - def::DefVariant(enum_id, variant_id, true) => { - if let &Some(ref base_expr) = base_expr { - span_err!(tcx.sess, base_expr.span, E0436, - "functional record update syntax requires a struct"); - fcx.write_error(base_expr.id); - } - check_struct_enum_variant(fcx, id, expr.span, enum_id, - variant_id, &fields[..]); - enum_id - } - def::DefTrait(def_id) => { - span_err!(tcx.sess, path.span, E0159, - "use of trait `{}` as a struct constructor", - pprust::path_to_string(path)); - check_struct_fields_on_error(fcx, - id, - &fields[..], - base_expr); - def_id - }, - def => { - // Verify that this was actually a struct. - let typ = fcx.ccx.tcx.lookup_item_type(def.def_id()); - match typ.ty.sty { - ty::TyStruct(struct_def, _) => { - check_struct_constructor(fcx, - id, - expr.span, - struct_def, - &fields[..], - base_expr.as_ref().map(|e| &**e)); - } - _ => { - span_err!(tcx.sess, path.span, E0071, - "`{}` does not name a structure", - pprust::path_to_string(path)); - check_struct_fields_on_error(fcx, - id, - &fields[..], - base_expr); - } - } - - def.def_id() - } - }; - - // Turn the path into a type and verify that that type unifies with - // the resulting structure type. This is needed to handle type - // parameters correctly. - let actual_structure_type = fcx.expr_ty(&*expr); - if !actual_structure_type.references_error() { - let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path); - match fcx.mk_subty(false, - infer::Misc(path.span), - actual_structure_type, - type_and_substs.ty) { - Ok(()) => {} - Err(type_error) => { - span_err!(fcx.tcx().sess, path.span, E0235, - "structure constructor specifies a \ - structure of type `{}`, but this \ - structure has type `{}`: {}", - fcx.infcx() - .ty_to_string(type_and_substs.ty), - fcx.infcx() - .ty_to_string( - actual_structure_type), - type_error); - tcx.note_and_explain_type_err(&type_error, path.span); - } - } - } - + check_expr_struct(fcx, expr, path, fields, base_expr); fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized); } ast::ExprField(ref base, ref field) => { @@ -3904,14 +3873,6 @@ fn have_disallowed_generic_consts<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } -fn constrain_path_type_parameters(fcx: &FnCtxt, - expr: &ast::Expr) -{ - fcx.opt_node_ty_substs(expr.id, |item_substs| { - fcx.add_default_region_param_bounds(&item_substs.substs, expr); - }); -} - impl<'tcx> Expectation<'tcx> { /// Provide an expectation for an rvalue expression given an *optional* /// hint, which is not required for type safety (the resulting type might @@ -4680,6 +4641,9 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } + debug!("instantiate_path: type of {:?} is {:?}", + node_id, + ty_substituted); fcx.write_ty(node_id, ty_substituted); fcx.write_substs(node_id, ty::ItemSubsts { substs: substs }); return;