Normalize types of fields in struct literals during type-checking.
Fixes #20535.
This commit is contained in:
parent
18f426e647
commit
cb98c3d93a
@ -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>(
|
||||
|
@ -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,
|
||||
|
29
src/test/run-pass/associated-types-ref-in-struct-literal.rs
Normal file
29
src/test/run-pass/associated-types-ref-in-struct-literal.rs
Normal file
@ -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 <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.
|
||||
|
||||
// Test associated type references in a struct literal. Issue #20535.
|
||||
|
||||
pub trait Foo {
|
||||
type Bar;
|
||||
}
|
||||
|
||||
impl Foo for int {
|
||||
type Bar = int;
|
||||
}
|
||||
|
||||
struct Thing<F: Foo> {
|
||||
a: F,
|
||||
b: F::Bar,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let thing = Thing{a: 1i, b: 2i};
|
||||
assert_eq!(thing.a + 1, thing.b);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user