Skip to content

Commit c5edd22

Browse files
committed
Rewrite the intrinsicck to take the parameter environment into account. Also fixes #20116.
1 parent 4946e1a commit c5edd22

File tree

7 files changed

+352
-160
lines changed

7 files changed

+352
-160
lines changed

src/librustc/middle/intrinsicck.rs

Lines changed: 192 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
use metadata::csearch;
1212
use middle::def::DefFn;
13-
use middle::subst::Subst;
13+
use middle::subst::{Subst, Substs, EnumeratedItems};
1414
use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn};
1515
use middle::ty::{mod, Ty};
16+
use util::ppaux::Repr;
1617

1718
use syntax::abi::RustIntrinsic;
1819
use syntax::ast::DefId;
@@ -23,52 +24,31 @@ use syntax::parse::token;
2324
use syntax::visit::Visitor;
2425
use syntax::visit;
2526

26-
fn type_size_is_affected_by_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>, typ: Ty<'tcx>)
27-
-> bool {
28-
let mut result = false;
29-
ty::maybe_walk_ty(typ, |typ| {
30-
match typ.sty {
31-
ty::ty_uniq(_) | ty::ty_ptr(_) | ty::ty_rptr(..) |
32-
ty::ty_bare_fn(..) | ty::ty_closure(..) => {
33-
false
34-
}
35-
ty::ty_param(_) => {
36-
result = true;
37-
// No need to continue; we now know the result.
38-
false
39-
}
40-
ty::ty_enum(did, substs) => {
41-
for enum_variant in (*ty::enum_variants(tcx, did)).iter() {
42-
for argument_type in enum_variant.args.iter() {
43-
let argument_type = argument_type.subst(tcx, substs);
44-
result = result ||
45-
type_size_is_affected_by_type_parameters(
46-
tcx,
47-
argument_type);
48-
}
49-
}
50-
51-
// Don't traverse substitutions.
52-
false
53-
}
54-
ty::ty_struct(did, substs) => {
55-
for field in ty::struct_fields(tcx, did, substs).iter() {
56-
result = result ||
57-
type_size_is_affected_by_type_parameters(tcx,
58-
field.mt.ty);
59-
}
60-
61-
// Don't traverse substitutions.
62-
false
63-
}
64-
_ => true,
65-
}
66-
});
67-
result
27+
pub fn check_crate(tcx: &ctxt) {
28+
let mut visitor = IntrinsicCheckingVisitor {
29+
tcx: tcx,
30+
param_envs: Vec::new(),
31+
dummy_sized_ty: ty::mk_int(),
32+
dummy_unsized_ty: ty::mk_vec(tcx, ty::mk_int(), None),
33+
};
34+
visit::walk_crate(&mut visitor, tcx.map.krate());
6835
}
6936

7037
struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
7138
tcx: &'a ctxt<'tcx>,
39+
40+
// As we traverse the AST, we keep a stack of the parameter
41+
// environments for each function we encounter. When we find a
42+
// call to `transmute`, we can check it in the context of the top
43+
// of the stack (which ought not to be empty).
44+
param_envs: Vec<ty::ParameterEnvironment<'tcx>>,
45+
46+
// Dummy sized/unsized types that use to substitute for type
47+
// parameters in order to estimate how big a type will be for any
48+
// possible instantiation of the type parameters in scope. See
49+
// `check_transmute` for more details.
50+
dummy_sized_ty: Ty<'tcx>,
51+
dummy_unsized_ty: Ty<'tcx>,
7252
}
7353

7454
impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
@@ -97,26 +77,175 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
9777
}
9878

9979
fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
100-
if type_size_is_affected_by_type_parameters(self.tcx, from) {
80+
// Find the parameter environment for the most recent function that
81+
// we entered.
82+
83+
let param_env = match self.param_envs.last() {
84+
Some(p) => p,
85+
None => {
86+
self.tcx.sess.span_bug(
87+
span,
88+
"transmute encountered outside of any fn");
89+
}
90+
};
91+
92+
// Simple case: no type parameters involved.
93+
if
94+
!ty::type_has_params(from) && !ty::type_has_self(from) &&
95+
!ty::type_has_params(to) && !ty::type_has_self(to)
96+
{
97+
let restriction = TransmuteRestriction {
98+
span: span,
99+
original_from: from,
100+
original_to: to,
101+
substituted_from: from,
102+
substituted_to: to,
103+
id: id,
104+
};
105+
self.push_transmute_restriction(restriction);
106+
return;
107+
}
108+
109+
// The rules around type parameters are a bit subtle. We are
110+
// checking these rules before monomorphization, so there may
111+
// be unsubstituted type parameters present in the
112+
// types. Obviously we cannot create LLVM types for those.
113+
// However, if a type parameter appears only indirectly (i.e.,
114+
// through a pointer), it does not necessarily affect the
115+
// size, so that should be allowed. The only catch is that we
116+
// DO want to be careful around unsized type parameters, since
117+
// fat pointers have a different size than a thin pointer, and
118+
// hence `&T` and `&U` have different sizes if `T : Sized` but
119+
// `U : Sized` does not hold.
120+
//
121+
// However, it's not as simple as checking whether `T :
122+
// Sized`, because even if `T : Sized` does not hold, that
123+
// just means that `T` *may* not be sized. After all, even a
124+
// type parameter `Sized? T` could be bound to a sized
125+
// type. (Issue #20116)
126+
//
127+
// To handle this, we first check for "interior" type
128+
// parameters, which are always illegal. If there are none of
129+
// those, then we know that the only way that all type
130+
// parameters `T` are referenced indirectly, e.g. via a
131+
// pointer type like `&T`. In that case, we only care whether
132+
// `T` is sized or not, because that influences whether `&T`
133+
// is a thin or fat pointer.
134+
//
135+
// One could imagine establishing a sophisticated constraint
136+
// system to ensure that the transmute is legal, but instead
137+
// we do something brutally dumb. We just substitute dummy
138+
// sized or unsized types for every type parameter in scope,
139+
// exhaustively checking all possible combinations. Here are some examples:
140+
//
141+
// ```
142+
// fn foo<T,U>() {
143+
// // T=int, U=int
144+
// }
145+
//
146+
// fn bar<Sized? T,U>() {
147+
// // T=int, U=int
148+
// // T=[int], U=int
149+
// }
150+
//
151+
// fn baz<Sized? T, Sized?U>() {
152+
// // T=int, U=int
153+
// // T=[int], U=int
154+
// // T=int, U=[int]
155+
// // T=[int], U=[int]
156+
// }
157+
// ```
158+
//
159+
// In all cases, we keep the original unsubstituted types
160+
// around for error reporting.
161+
162+
let from_tc = ty::type_contents(self.tcx, from);
163+
let to_tc = ty::type_contents(self.tcx, to);
164+
if from_tc.interior_param() || to_tc.interior_param() {
101165
span_err!(self.tcx.sess, span, E0139,
102-
"cannot transmute from a type that contains type parameters");
166+
"cannot transmute to or from a type that contains \
167+
type parameters in its interior");
168+
return;
103169
}
104-
if type_size_is_affected_by_type_parameters(self.tcx, to) {
105-
span_err!(self.tcx.sess, span, E0140,
106-
"cannot transmute to a type that contains type parameters");
170+
171+
let mut substs = param_env.free_substs.clone();
172+
self.with_each_combination(
173+
param_env,
174+
param_env.free_substs.types.iter_enumerated(),
175+
&mut substs,
176+
&mut |substs| {
177+
let restriction = TransmuteRestriction {
178+
span: span,
179+
original_from: from,
180+
original_to: to,
181+
substituted_from: from.subst(self.tcx, substs),
182+
substituted_to: to.subst(self.tcx, substs),
183+
id: id,
184+
};
185+
self.push_transmute_restriction(restriction);
186+
});
187+
}
188+
189+
fn with_each_combination(&self,
190+
param_env: &ty::ParameterEnvironment<'tcx>,
191+
mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
192+
substs: &mut Substs<'tcx>,
193+
callback: &mut FnMut(&Substs<'tcx>))
194+
{
195+
// This parameter invokes `callback` many times with different
196+
// substitutions that replace all the parameters in scope with
197+
// either `int` or `[int]`, depending on whether the type
198+
// parameter is known to be sized. See big comment above for
199+
// an explanation of why this is a reasonable thing to do.
200+
201+
match types_in_scope.next() {
202+
None => {
203+
debug!("with_each_combination(substs={})",
204+
substs.repr(self.tcx));
205+
206+
callback.call_mut((substs,));
207+
}
208+
209+
Some((space, index, &param_ty)) => {
210+
debug!("with_each_combination: space={}, index={}, param_ty={}",
211+
space, index, param_ty.repr(self.tcx));
212+
213+
if !ty::type_is_sized(self.tcx, param_ty, param_env) {
214+
debug!("with_each_combination: param_ty is not known to be sized");
215+
216+
substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
217+
self.with_each_combination(param_env, types_in_scope.clone(), substs, callback);
218+
}
219+
220+
substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
221+
self.with_each_combination(param_env, types_in_scope, substs, callback);
222+
}
107223
}
224+
}
108225

109-
let restriction = TransmuteRestriction {
110-
span: span,
111-
from: from,
112-
to: to,
113-
id: id,
114-
};
226+
fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
227+
debug!("Pushing transmute restriction: {}", restriction.repr(self.tcx));
115228
self.tcx.transmute_restrictions.borrow_mut().push(restriction);
116229
}
117230
}
118231

119232
impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
233+
fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
234+
b: &'v ast::Block, s: Span, id: ast::NodeId) {
235+
match fk {
236+
visit::FkItemFn(..) | visit::FkMethod(..) => {
237+
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
238+
self.param_envs.push(param_env);
239+
visit::walk_fn(self, fk, fd, b, s);
240+
self.param_envs.pop();
241+
}
242+
visit::FkFnBlock(..) => {
243+
visit::walk_fn(self, fk, fd, b, s);
244+
}
245+
}
246+
247+
}
248+
120249
fn visit_expr(&mut self, expr: &ast::Expr) {
121250
if let ast::ExprPath(..) = expr.node {
122251
match ty::resolve_expr(self.tcx, expr) {
@@ -144,7 +273,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
144273
}
145274
}
146275

147-
pub fn check_crate(tcx: &ctxt) {
148-
visit::walk_crate(&mut IntrinsicCheckingVisitor { tcx: tcx },
149-
tcx.map.krate());
276+
impl<'tcx> Repr<'tcx> for TransmuteRestriction<'tcx> {
277+
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
278+
format!("TransmuteRestriction(id={}, original=({},{}), substituted=({},{}))",
279+
self.id,
280+
self.original_from.repr(tcx),
281+
self.original_to.repr(tcx),
282+
self.substituted_from.repr(tcx),
283+
self.substituted_to.repr(tcx))
284+
}
150285
}

src/librustc/middle/subst.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ impl<T> VecPerParamSpace<T> {
487487
}
488488
}
489489

490+
#[deriving(Clone)]
490491
pub struct EnumeratedItems<'a,T:'a> {
491492
vec: &'a VecPerParamSpace<T>,
492493
space_index: uint,

0 commit comments

Comments
 (0)