Skip to content

Commit 73b8f60

Browse files
committed
librustc: Allow trait bounds on structures and enumerations, and check
them during kind checking. This implements RFC #11. Closes #15759.
1 parent f8e0ede commit 73b8f60

23 files changed

+574
-41
lines changed

src/librustc/middle/kind.rs

+191-7
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@
1212
use middle::freevars::freevar_entry;
1313
use middle::freevars;
1414
use middle::subst;
15+
use middle::ty::ParameterEnvironment;
1516
use middle::ty;
16-
use middle::ty_fold;
1717
use middle::ty_fold::TypeFoldable;
18-
use middle::typeck;
18+
use middle::ty_fold;
19+
use middle::typeck::check::vtable;
1920
use middle::typeck::{MethodCall, NoAdjustment};
21+
use middle::typeck;
2022
use util::ppaux::{Repr, ty_to_string};
2123
use util::ppaux::UserString;
2224

25+
use std::collections::HashSet;
2326
use syntax::ast::*;
27+
use syntax::ast_util;
2428
use syntax::attr;
2529
use syntax::codemap::Span;
2630
use syntax::print::pprust::{expr_to_string, ident_to_string};
27-
use syntax::{visit};
2831
use syntax::visit::Visitor;
32+
use syntax::visit;
2933

3034
// Kind analysis pass.
3135
//
@@ -47,13 +51,13 @@ use syntax::visit::Visitor;
4751
// primitives in the stdlib are explicitly annotated to only take sendable
4852
// types.
4953

50-
#[deriving(Clone)]
5154
pub struct Context<'a> {
5255
tcx: &'a ty::ctxt,
56+
struct_and_enum_bounds_checked: HashSet<ty::t>,
57+
parameter_environments: Vec<ParameterEnvironment>,
5358
}
5459

5560
impl<'a> Visitor<()> for Context<'a> {
56-
5761
fn visit_expr(&mut self, ex: &Expr, _: ()) {
5862
check_expr(self, ex);
5963
}
@@ -74,12 +78,18 @@ impl<'a> Visitor<()> for Context<'a> {
7478
fn visit_pat(&mut self, p: &Pat, _: ()) {
7579
check_pat(self, p);
7680
}
81+
82+
fn visit_local(&mut self, l: &Local, _: ()) {
83+
check_local(self, l);
84+
}
7785
}
7886

7987
pub fn check_crate(tcx: &ty::ctxt,
8088
krate: &Crate) {
8189
let mut ctx = Context {
8290
tcx: tcx,
91+
struct_and_enum_bounds_checked: HashSet::new(),
92+
parameter_environments: Vec::new(),
8393
};
8494
visit::walk_crate(&mut ctx, krate, ());
8595
tcx.sess.abort_if_errors();
@@ -165,12 +175,90 @@ fn check_item(cx: &mut Context, item: &Item) {
165175
match item.node {
166176
ItemImpl(_, Some(ref trait_ref), ref self_type, _) => {
167177
check_impl_of_trait(cx, item, trait_ref, &**self_type);
178+
179+
let parameter_environment =
180+
ParameterEnvironment::for_item(cx.tcx, item.id);
181+
cx.parameter_environments.push(parameter_environment);
182+
183+
// Check bounds on the `self` type.
184+
check_bounds_on_structs_or_enums_in_type_if_possible(
185+
cx,
186+
item.span,
187+
ty::node_id_to_type(cx.tcx, item.id));
188+
189+
// Check bounds on the trait ref.
190+
match ty::impl_trait_ref(cx.tcx,
191+
ast_util::local_def(item.id)) {
192+
None => {}
193+
Some(trait_ref) => {
194+
check_bounds_on_structs_or_enums_in_trait_ref(
195+
cx,
196+
item.span,
197+
&*trait_ref);
198+
}
199+
}
200+
201+
drop(cx.parameter_environments.pop());
202+
}
203+
ItemEnum(..) => {
204+
let parameter_environment =
205+
ParameterEnvironment::for_item(cx.tcx, item.id);
206+
cx.parameter_environments.push(parameter_environment);
207+
208+
let def_id = ast_util::local_def(item.id);
209+
for variant in ty::enum_variants(cx.tcx, def_id).iter() {
210+
for arg in variant.args.iter() {
211+
check_bounds_on_structs_or_enums_in_type_if_possible(
212+
cx,
213+
item.span,
214+
*arg)
215+
}
216+
}
217+
218+
drop(cx.parameter_environments.pop());
219+
}
220+
ItemStruct(..) => {
221+
let parameter_environment =
222+
ParameterEnvironment::for_item(cx.tcx, item.id);
223+
cx.parameter_environments.push(parameter_environment);
224+
225+
let def_id = ast_util::local_def(item.id);
226+
for field in ty::lookup_struct_fields(cx.tcx, def_id).iter() {
227+
check_bounds_on_structs_or_enums_in_type_if_possible(
228+
cx,
229+
item.span,
230+
ty::node_id_to_type(cx.tcx, field.id.node))
231+
}
232+
233+
drop(cx.parameter_environments.pop());
234+
235+
}
236+
ItemStatic(..) => {
237+
/*let parameter_environment =
238+
ParameterEnvironment::for_item(cx.tcx, item.id);
239+
cx.parameter_environments.push(parameter_environment);
240+
241+
check_bounds_on_structs_or_enums_in_type_if_possible(
242+
cx,
243+
item.span,
244+
ty::node_id_to_type(cx.tcx, item.id));
245+
246+
drop(cx.parameter_environments.pop());*/
168247
}
169248
_ => {}
170249
}
171250
}
251+
252+
visit::walk_item(cx, item, ())
253+
}
254+
255+
fn check_local(cx: &mut Context, local: &Local) {
256+
check_bounds_on_structs_or_enums_in_type_if_possible(
257+
cx,
258+
local.span,
259+
ty::node_id_to_type(cx.tcx, local.id));
172260

173-
visit::walk_item(cx, item, ());
261+
visit::walk_local(cx, local, ())
174262
}
175263

176264
// Yields the appropriate function to check the kind of closed over
@@ -254,7 +342,25 @@ fn check_fn(
254342
});
255343
});
256344

257-
visit::walk_fn(cx, fk, decl, body, sp, ());
345+
match *fk {
346+
visit::FkFnBlock(..) => {
347+
let ty = ty::node_id_to_type(cx.tcx, fn_id);
348+
check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty);
349+
350+
visit::walk_fn(cx, fk, decl, body, sp, ())
351+
}
352+
visit::FkItemFn(..) | visit::FkMethod(..) => {
353+
let parameter_environment = ParameterEnvironment::for_item(cx.tcx,
354+
fn_id);
355+
cx.parameter_environments.push(parameter_environment);
356+
357+
let ty = ty::node_id_to_type(cx.tcx, fn_id);
358+
check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty);
359+
360+
visit::walk_fn(cx, fk, decl, body, sp, ());
361+
drop(cx.parameter_environments.pop());
362+
}
363+
}
258364
}
259365

260366
pub fn check_expr(cx: &mut Context, e: &Expr) {
@@ -263,6 +369,13 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
263369
// Handle any kind bounds on type parameters
264370
check_bounds_on_type_parameters(cx, e);
265371

372+
// Check bounds on structures or enumerations in the type of the
373+
// expression.
374+
let expression_type = ty::expr_ty(cx.tcx, e);
375+
check_bounds_on_structs_or_enums_in_type_if_possible(cx,
376+
e.span,
377+
expression_type);
378+
266379
match e.node {
267380
ExprBox(ref loc, ref interior) => {
268381
let def = ty::resolve_expr(cx.tcx, &**loc);
@@ -483,6 +596,7 @@ fn check_ty(cx: &mut Context, aty: &Ty) {
483596
}
484597
_ => {}
485598
}
599+
486600
visit::walk_ty(cx, aty, ());
487601
}
488602

@@ -519,6 +633,76 @@ pub fn check_typaram_bounds(cx: &Context,
519633
});
520634
}
521635

636+
fn check_bounds_on_structs_or_enums_in_type_if_possible(cx: &mut Context,
637+
span: Span,
638+
ty: ty::t) {
639+
// If we aren't in a function, structure, or enumeration context, we don't
640+
// have enough information to ensure that bounds on structures or
641+
// enumerations are satisfied. So we don't perform the check.
642+
if cx.parameter_environments.len() == 0 {
643+
return
644+
}
645+
646+
// If we've already checked for this type, don't do it again. This
647+
// massively speeds up kind checking.
648+
if cx.struct_and_enum_bounds_checked.contains(&ty) {
649+
return
650+
}
651+
cx.struct_and_enum_bounds_checked.insert(ty);
652+
653+
ty::walk_ty(ty, |ty| {
654+
match ty::get(ty).sty {
655+
ty::ty_struct(type_id, ref substs) |
656+
ty::ty_enum(type_id, ref substs) => {
657+
let polytype = ty::lookup_item_type(cx.tcx, type_id);
658+
659+
// Check builtin bounds.
660+
for (ty, type_param_def) in substs.types
661+
.iter()
662+
.zip(polytype.generics
663+
.types
664+
.iter()) {
665+
check_typaram_bounds(cx, span, *ty, type_param_def)
666+
}
667+
668+
// Check trait bounds.
669+
let parameter_environment =
670+
cx.parameter_environments.get(cx.parameter_environments
671+
.len() - 1);
672+
debug!(
673+
"check_bounds_on_structs_or_enums_in_type_if_possible(): \
674+
checking {}",
675+
ty.repr(cx.tcx));
676+
vtable::check_param_bounds(cx.tcx,
677+
span,
678+
parameter_environment,
679+
&polytype.generics.types,
680+
substs,
681+
|missing| {
682+
cx.tcx
683+
.sess
684+
.span_err(span,
685+
format!("instantiating a type parameter with \
686+
an incompatible type `{}`, which \
687+
does not fulfill `{}`",
688+
ty_to_string(cx.tcx, ty),
689+
missing.user_string(
690+
cx.tcx)).as_slice());
691+
})
692+
}
693+
_ => {}
694+
}
695+
});
696+
}
697+
698+
fn check_bounds_on_structs_or_enums_in_trait_ref(cx: &mut Context,
699+
span: Span,
700+
trait_ref: &ty::TraitRef) {
701+
for ty in trait_ref.substs.types.iter() {
702+
check_bounds_on_structs_or_enums_in_type_if_possible(cx, span, *ty)
703+
}
704+
}
705+
522706
pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t,
523707
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
524708
{

src/librustc/middle/ty.rs

+79-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use std::rc::Rc;
5252
use std::collections::{HashMap, HashSet};
5353
use syntax::abi;
5454
use syntax::ast::*;
55-
use syntax::ast_util::{is_local, lit_is_str};
55+
use syntax::ast_util::{PostExpansionMethod, is_local, lit_is_str};
5656
use syntax::ast_util;
5757
use syntax::attr;
5858
use syntax::attr::AttrMetaMethods;
@@ -1080,6 +1080,84 @@ pub struct ParameterEnvironment {
10801080
pub bounds: VecPerParamSpace<ParamBounds>,
10811081
}
10821082

1083+
impl ParameterEnvironment {
1084+
pub fn for_item(cx: &ctxt, id: NodeId) -> ParameterEnvironment {
1085+
match cx.map.find(id) {
1086+
Some(ast_map::NodeImplItem(ref impl_item)) => {
1087+
match **impl_item {
1088+
ast::MethodImplItem(ref method) => {
1089+
let method_def_id = ast_util::local_def(id);
1090+
match ty::impl_or_trait_item(cx, method_def_id) {
1091+
MethodTraitItem(ref method_ty) => {
1092+
let method_generics = &method_ty.generics;
1093+
construct_parameter_environment(
1094+
cx,
1095+
method_generics,
1096+
method.pe_body().id)
1097+
}
1098+
}
1099+
}
1100+
}
1101+
}
1102+
Some(ast_map::NodeTraitItem(trait_method)) => {
1103+
match *trait_method {
1104+
ast::RequiredMethod(ref required) => {
1105+
cx.sess.span_bug(required.span,
1106+
"ParameterEnvironment::from_item():
1107+
can't create a parameter \
1108+
environment for required trait \
1109+
methods")
1110+
}
1111+
ast::ProvidedMethod(ref method) => {
1112+
let method_def_id = ast_util::local_def(id);
1113+
match ty::impl_or_trait_item(cx, method_def_id) {
1114+
MethodTraitItem(ref method_ty) => {
1115+
let method_generics = &method_ty.generics;
1116+
construct_parameter_environment(
1117+
cx,
1118+
method_generics,
1119+
method.pe_body().id)
1120+
}
1121+
}
1122+
}
1123+
}
1124+
}
1125+
Some(ast_map::NodeItem(item)) => {
1126+
match item.node {
1127+
ast::ItemFn(_, _, _, _, ref body) => {
1128+
// We assume this is a function.
1129+
let fn_def_id = ast_util::local_def(id);
1130+
let fn_pty = ty::lookup_item_type(cx, fn_def_id);
1131+
1132+
construct_parameter_environment(cx,
1133+
&fn_pty.generics,
1134+
body.id)
1135+
}
1136+
ast::ItemEnum(..) |
1137+
ast::ItemStruct(..) |
1138+
ast::ItemImpl(..) |
1139+
ast::ItemStatic(..) => {
1140+
let def_id = ast_util::local_def(id);
1141+
let pty = ty::lookup_item_type(cx, def_id);
1142+
construct_parameter_environment(cx, &pty.generics, id)
1143+
}
1144+
_ => {
1145+
cx.sess.span_bug(item.span,
1146+
"ParameterEnvironment::from_item():
1147+
can't create a parameter \
1148+
environment for this kind of item")
1149+
}
1150+
}
1151+
}
1152+
_ => {
1153+
cx.sess.bug(format!("ParameterEnvironment::from_item(): \
1154+
`{}` is not an item",
1155+
cx.map.node_to_string(id)).as_slice())
1156+
}
1157+
}
1158+
}
1159+
}
1160+
10831161
/// A polytype.
10841162
///
10851163
/// - `generics`: the set of type parameters and their bounds

src/librustc/middle/typeck/astconv.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
5757
use middle::subst::{VecPerParamSpace};
5858
use middle::ty;
5959
use middle::ty_fold::TypeFolder;
60+
use middle::typeck::check;
6061
use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope};
6162
use middle::typeck::rscope::RegionScope;
6263
use middle::typeck::{TypeAndSubsts, infer, lookup_def_tcx, rscope};
@@ -879,7 +880,8 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
879880
}
880881
}
881882
ast::TyFixedLengthVec(ty, e) => {
882-
typeck::write_ty_to_tcx(tcx, e.id, ty::mk_uint());
883+
check::check_const_in_type(tcx, &*e, ty::mk_uint());
884+
883885
match const_eval::eval_const_expr_partial(tcx, &*e) {
884886
Ok(ref r) => {
885887
match *r {

0 commit comments

Comments
 (0)