From cb98c3d93a4c0e79ae87a04a6d3a439ac8ed87b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 18:45:36 -0500 Subject: [PATCH] Normalize types of fields in struct literals during type-checking. Fixes #20535. --- src/librustc_typeck/astconv.rs | 47 +--------------- src/librustc_typeck/check/mod.rs | 53 +++++++++++++++---- .../associated-types-ref-in-struct-literal.rs | 29 ++++++++++ 3 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 src/test/run-pass/associated-types-ref-in-struct-literal.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 265ebe00d53..3534d3ff3af 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -52,7 +52,6 @@ use middle::const_eval; use middle::def; use middle::resolve_lifetime as rl; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; -use middle::subst::{VecPerParamSpace}; use middle::traits; use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty}; use rscope::{self, UnelidableRscope, RegionScope, SpecificRscope, @@ -244,7 +243,7 @@ pub fn opt_ast_region_to_region<'tcx>( /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, /// returns an appropriate set of substitutions for this particular reference to `I`. -fn ast_path_substs_for_ty<'tcx>( +pub fn ast_path_substs_for_ty<'tcx>( this: &AstConv<'tcx>, rscope: &RegionScope, decl_generics: &ty::Generics<'tcx>, @@ -762,50 +761,6 @@ pub fn ast_path_to_ty<'tcx>( TypeAndSubsts { substs: substs, ty: 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. -pub fn ast_path_to_ty_relaxed<'tcx>( - this: &AstConv<'tcx>, - rscope: &RegionScope, - did: ast::DefId, - path: &ast::Path) - -> TypeAndSubsts<'tcx> -{ - let tcx = this.tcx(); - let ty::TypeScheme { - generics, - ty: decl_ty - } = this.get_item_type_scheme(did); - - let wants_params = - generics.has_type_params(TypeSpace) || generics.has_region_params(TypeSpace); - - let needs_defaults = - wants_params && - path.segments.iter().all(|s| s.parameters.is_empty()); - - let substs = if needs_defaults { - let type_params: Vec<_> = range(0, generics.types.len(TypeSpace)) - .map(|_| this.ty_infer(path.span)).collect(); - let region_params = - rscope.anon_regions(path.span, generics.regions.len(TypeSpace)) - .unwrap(); - Substs::new(VecPerParamSpace::params_from_type(type_params), - VecPerParamSpace::params_from_type(region_params)) - } else { - ast_path_substs_for_ty(this, rscope, &generics, path) - }; - - let ty = decl_ty.subst(tcx, &substs); - TypeAndSubsts { - substs: substs, - ty: ty, - } -} - /// Converts the given AST type to a built-in type. A "built-in type" is, at /// present, either a core numeric type, a string, or `Box`. pub fn ast_ty_to_builtin_ty<'tcx>( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9563dd45ca2..87175279bae 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,7 +90,7 @@ use middle::mem_categorization as mc; use middle::mem_categorization::McResult; use middle::pat_util::{self, pat_id_map}; use middle::region::CodeExtent; -use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; +use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace}; use middle::traits; use middle::ty::{FnSig, VariantInfo, TypeScheme}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; @@ -1947,6 +1947,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// 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> + { + let tcx = self.tcx(); + + let ty::TypeScheme { generics, ty: decl_ty } = ty::lookup_item_type(tcx, did); + + let wants_params = + generics.has_type_params(TypeSpace) || generics.has_region_params(TypeSpace); + + let needs_defaults = + wants_params && + path.segments.iter().all(|s| s.parameters.is_empty()); + + let substs = if needs_defaults { + let tps = + self.infcx().next_ty_vars(generics.types.len(TypeSpace)); + let rps = + self.infcx().region_vars_for_defs(path.span, + generics.regions.get_slice(TypeSpace)); + Substs::new_type(tps, rps) + } else { + astconv::ast_path_substs_for_ty(self, self, &generics, path) + }; + + let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty); + + TypeAndSubsts { substs: substs, ty: ty } + } + pub fn write_nil(&self, node_id: ast::NodeId) { self.write_ty(node_id, ty::mk_nil(self.tcx())); } @@ -3490,17 +3527,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, expected_field_type = ty::lookup_field_type( tcx, class_id, field_id, substitutions); + expected_field_type = + fcx.normalize_associated_types_in( + field.span, &expected_field_type); class_field_map.insert( field.ident.node.name, (field_id, true)); fields_found += 1; } } + // Make sure to give a type to the field even if there's // an error, so we can continue typechecking - check_expr_coercable_to_type( - fcx, - &*field.expr, - expected_field_type); + check_expr_coercable_to_type(fcx, &*field.expr, expected_field_type); } if error_happened { @@ -4149,10 +4187,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // parameters correctly. let actual_structure_type = fcx.expr_ty(&*expr); if !ty::type_is_error(actual_structure_type) { - let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx, - fcx, - struct_id, - path); + let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path); match fcx.mk_subty(false, infer::Misc(path.span), actual_structure_type, diff --git a/src/test/run-pass/associated-types-ref-in-struct-literal.rs b/src/test/run-pass/associated-types-ref-in-struct-literal.rs new file mode 100644 index 00000000000..b51d44a0c24 --- /dev/null +++ b/src/test/run-pass/associated-types-ref-in-struct-literal.rs @@ -0,0 +1,29 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test associated type references in a struct literal. Issue #20535. + +pub trait Foo { + type Bar; +} + +impl Foo for int { + type Bar = int; +} + +struct Thing { + a: F, + b: F::Bar, +} + +fn main() { + let thing = Thing{a: 1i, b: 2i}; + assert_eq!(thing.a + 1, thing.b); +}