Skip to content

Commit

Permalink
librustc: Allow trait bounds on structures and enumerations, and check
Browse files Browse the repository at this point in the history
them during kind checking.

This implements RFC #11.

Closes #15759.
  • Loading branch information
pcwalton committed Aug 17, 2014
1 parent cb9c1e0 commit 086a5ca
Show file tree
Hide file tree
Showing 26 changed files with 622 additions and 43 deletions.
34 changes: 34 additions & 0 deletions src/liblibc/lib.rs
Expand Up @@ -1456,13 +1456,24 @@ pub mod types {
pub Data4: [BYTE, ..8],
}

// NOTE(pcwalton, stage0): Remove after snapshot (typeck bug
// workaround).
#[cfg(stage0)]
pub struct WSAPROTOCOLCHAIN {
pub ChainLen: c_int,
pub ChainEntries: [DWORD, ..MAX_PROTOCOL_CHAIN],
}
#[cfg(not(stage0))]
pub struct WSAPROTOCOLCHAIN {
pub ChainLen: c_int,
pub ChainEntries: [DWORD, ..MAX_PROTOCOL_CHAIN as uint],
}

pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN;

// NOTE(pcwalton, stage0): Remove after snapshot (typeck bug
// workaround).
#[cfg(stage0)]
pub struct WSAPROTOCOL_INFO {
pub dwServiceFlags1: DWORD,
pub dwServiceFlags2: DWORD,
Expand All @@ -1485,6 +1496,29 @@ pub mod types {
pub dwProviderReserved: DWORD,
pub szProtocol: [u8, ..WSAPROTOCOL_LEN+1],
}
#[cfg(not(stage0))]
pub struct WSAPROTOCOL_INFO {
pub dwServiceFlags1: DWORD,
pub dwServiceFlags2: DWORD,
pub dwServiceFlags3: DWORD,
pub dwServiceFlags4: DWORD,
pub dwProviderFlags: DWORD,
pub ProviderId: GUID,
pub dwCatalogEntryId: DWORD,
pub ProtocolChain: WSAPROTOCOLCHAIN,
pub iVersion: c_int,
pub iAddressFamily: c_int,
pub iMaxSockAddr: c_int,
pub iMinSockAddr: c_int,
pub iSocketType: c_int,
pub iProtocol: c_int,
pub iProtocolMaxOffset: c_int,
pub iNetworkByteOrder: c_int,
pub iSecurityScheme: c_int,
pub dwMessageSize: DWORD,
pub dwProviderReserved: DWORD,
pub szProtocol: [u8, ..(WSAPROTOCOL_LEN as uint) + 1u],
}

pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;

Expand Down
12 changes: 12 additions & 0 deletions src/librustc/middle/const_eval.rs
Expand Up @@ -17,6 +17,7 @@ use middle::def;
use middle::pat_util::def_to_path;
use middle::ty;
use middle::typeck::astconv;
use middle::typeck::check;
use util::nodemap::{DefIdMap};

use syntax::ast::*;
Expand Down Expand Up @@ -274,6 +275,17 @@ impl<'a> ConstEvalVisitor<'a> {
}

impl<'a> Visitor<()> for ConstEvalVisitor<'a> {
fn visit_ty(&mut self, t: &Ty, _: ()) {
match t.node {
TyFixedLengthVec(_, expr) => {
check::check_const_in_type(self.tcx, &*expr, ty::mk_uint());
}
_ => {}
}

visit::walk_ty(self, t, ());
}

fn visit_expr_post(&mut self, e: &Expr, _: ()) {
self.classify(e);
}
Expand Down
198 changes: 191 additions & 7 deletions src/librustc/middle/kind.rs
Expand Up @@ -12,20 +12,24 @@
use middle::freevars::freevar_entry;
use middle::freevars;
use middle::subst;
use middle::ty::ParameterEnvironment;
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::TypeFoldable;
use middle::typeck;
use middle::ty_fold;
use middle::typeck::check::vtable;
use middle::typeck::{MethodCall, NoAdjustment};
use middle::typeck;
use util::ppaux::{Repr, ty_to_string};
use util::ppaux::UserString;

use std::collections::HashSet;
use syntax::ast::*;
use syntax::ast_util;
use syntax::attr;
use syntax::codemap::Span;
use syntax::print::pprust::{expr_to_string, ident_to_string};
use syntax::{visit};
use syntax::visit::Visitor;
use syntax::visit;

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

#[deriving(Clone)]
pub struct Context<'a> {
tcx: &'a ty::ctxt,
struct_and_enum_bounds_checked: HashSet<ty::t>,
parameter_environments: Vec<ParameterEnvironment>,
}

impl<'a> Visitor<()> for Context<'a> {

fn visit_expr(&mut self, ex: &Expr, _: ()) {
check_expr(self, ex);
}
Expand All @@ -74,12 +78,18 @@ impl<'a> Visitor<()> for Context<'a> {
fn visit_pat(&mut self, p: &Pat, _: ()) {
check_pat(self, p);
}

fn visit_local(&mut self, l: &Local, _: ()) {
check_local(self, l);
}
}

pub fn check_crate(tcx: &ty::ctxt,
krate: &Crate) {
let mut ctx = Context {
tcx: tcx,
struct_and_enum_bounds_checked: HashSet::new(),
parameter_environments: Vec::new(),
};
visit::walk_crate(&mut ctx, krate, ());
tcx.sess.abort_if_errors();
Expand Down Expand Up @@ -165,12 +175,90 @@ fn check_item(cx: &mut Context, item: &Item) {
match item.node {
ItemImpl(_, Some(ref trait_ref), ref self_type, _) => {
check_impl_of_trait(cx, item, trait_ref, &**self_type);

let parameter_environment =
ParameterEnvironment::for_item(cx.tcx, item.id);
cx.parameter_environments.push(parameter_environment);

// Check bounds on the `self` type.
check_bounds_on_structs_or_enums_in_type_if_possible(
cx,
item.span,
ty::node_id_to_type(cx.tcx, item.id));

// Check bounds on the trait ref.
match ty::impl_trait_ref(cx.tcx,
ast_util::local_def(item.id)) {
None => {}
Some(trait_ref) => {
check_bounds_on_structs_or_enums_in_trait_ref(
cx,
item.span,
&*trait_ref);
}
}

drop(cx.parameter_environments.pop());
}
ItemEnum(..) => {
let parameter_environment =
ParameterEnvironment::for_item(cx.tcx, item.id);
cx.parameter_environments.push(parameter_environment);

let def_id = ast_util::local_def(item.id);
for variant in ty::enum_variants(cx.tcx, def_id).iter() {
for arg in variant.args.iter() {
check_bounds_on_structs_or_enums_in_type_if_possible(
cx,
item.span,
*arg)
}
}

drop(cx.parameter_environments.pop());
}
ItemStruct(..) => {
let parameter_environment =
ParameterEnvironment::for_item(cx.tcx, item.id);
cx.parameter_environments.push(parameter_environment);

let def_id = ast_util::local_def(item.id);
for field in ty::lookup_struct_fields(cx.tcx, def_id).iter() {
check_bounds_on_structs_or_enums_in_type_if_possible(
cx,
item.span,
ty::node_id_to_type(cx.tcx, field.id.node))
}

drop(cx.parameter_environments.pop());

}
ItemStatic(..) => {
let parameter_environment =
ParameterEnvironment::for_item(cx.tcx, item.id);
cx.parameter_environments.push(parameter_environment);

check_bounds_on_structs_or_enums_in_type_if_possible(
cx,
item.span,
ty::node_id_to_type(cx.tcx, item.id));

drop(cx.parameter_environments.pop());
}
_ => {}
}
}

visit::walk_item(cx, item, ());
visit::walk_item(cx, item, ())
}

fn check_local(cx: &mut Context, local: &Local) {
check_bounds_on_structs_or_enums_in_type_if_possible(
cx,
local.span,
ty::node_id_to_type(cx.tcx, local.id));

visit::walk_local(cx, local, ())
}

// Yields the appropriate function to check the kind of closed over
Expand Down Expand Up @@ -254,7 +342,25 @@ fn check_fn(
});
});

visit::walk_fn(cx, fk, decl, body, sp, ());
match *fk {
visit::FkFnBlock(..) => {
let ty = ty::node_id_to_type(cx.tcx, fn_id);
check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty);

visit::walk_fn(cx, fk, decl, body, sp, ())
}
visit::FkItemFn(..) | visit::FkMethod(..) => {
let parameter_environment = ParameterEnvironment::for_item(cx.tcx,
fn_id);
cx.parameter_environments.push(parameter_environment);

let ty = ty::node_id_to_type(cx.tcx, fn_id);
check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty);

visit::walk_fn(cx, fk, decl, body, sp, ());
drop(cx.parameter_environments.pop());
}
}
}

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

// Check bounds on structures or enumerations in the type of the
// expression.
let expression_type = ty::expr_ty(cx.tcx, e);
check_bounds_on_structs_or_enums_in_type_if_possible(cx,
e.span,
expression_type);

match e.node {
ExprBox(ref loc, ref interior) => {
let def = ty::resolve_expr(cx.tcx, &**loc);
Expand Down Expand Up @@ -483,6 +596,7 @@ fn check_ty(cx: &mut Context, aty: &Ty) {
}
_ => {}
}

visit::walk_ty(cx, aty, ());
}

Expand Down Expand Up @@ -519,6 +633,76 @@ pub fn check_typaram_bounds(cx: &Context,
});
}

fn check_bounds_on_structs_or_enums_in_type_if_possible(cx: &mut Context,
span: Span,
ty: ty::t) {
// If we aren't in a function, structure, or enumeration context, we don't
// have enough information to ensure that bounds on structures or
// enumerations are satisfied. So we don't perform the check.
if cx.parameter_environments.len() == 0 {
return
}

// If we've already checked for this type, don't do it again. This
// massively speeds up kind checking.
if cx.struct_and_enum_bounds_checked.contains(&ty) {
return
}
cx.struct_and_enum_bounds_checked.insert(ty);

ty::walk_ty(ty, |ty| {
match ty::get(ty).sty {
ty::ty_struct(type_id, ref substs) |
ty::ty_enum(type_id, ref substs) => {
let polytype = ty::lookup_item_type(cx.tcx, type_id);

// Check builtin bounds.
for (ty, type_param_def) in substs.types
.iter()
.zip(polytype.generics
.types
.iter()) {
check_typaram_bounds(cx, span, *ty, type_param_def)
}

// Check trait bounds.
let parameter_environment =
cx.parameter_environments.get(cx.parameter_environments
.len() - 1);
debug!(
"check_bounds_on_structs_or_enums_in_type_if_possible(): \
checking {}",
ty.repr(cx.tcx));
vtable::check_param_bounds(cx.tcx,
span,
parameter_environment,
&polytype.generics.types,
substs,
|missing| {
cx.tcx
.sess
.span_err(span,
format!("instantiating a type parameter with \
an incompatible type `{}`, which \
does not fulfill `{}`",
ty_to_string(cx.tcx, ty),
missing.user_string(
cx.tcx)).as_slice());
})
}
_ => {}
}
});
}

fn check_bounds_on_structs_or_enums_in_trait_ref(cx: &mut Context,
span: Span,
trait_ref: &ty::TraitRef) {
for ty in trait_ref.substs.types.iter() {
check_bounds_on_structs_or_enums_in_type_if_possible(cx, span, *ty)
}
}

pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t,
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
{
Expand Down

5 comments on commit 086a5ca

@bors
Copy link
Contributor

@bors bors commented on 086a5ca Aug 17, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from nikomatsakis
at pcwalton@086a5ca

@bors
Copy link
Contributor

@bors bors commented on 086a5ca Aug 17, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging pcwalton/rust/kindck-traits = 086a5ca into auto

@bors
Copy link
Contributor

@bors bors commented on 086a5ca Aug 17, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pcwalton/rust/kindck-traits = 086a5ca merged ok, testing candidate = 921240e

@bors
Copy link
Contributor

@bors bors commented on 086a5ca Aug 17, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 921240e

Please sign in to comment.