Skip to content

Commit 3353371

Browse files
committed
rollup merge of #20517: nikomatsakis/safety-issue-19997
Fixes various safety issues. r? @aturon
2 parents cc0697e + dbfa054 commit 3353371

9 files changed

+285
-27
lines changed

src/librustc/middle/ty.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,10 @@ impl<'tcx> Generics<'tcx> {
17761776
!self.regions.is_empty_in(space)
17771777
}
17781778

1779+
pub fn is_empty(&self) -> bool {
1780+
self.types.is_empty() && self.regions.is_empty()
1781+
}
1782+
17791783
pub fn to_bounds(&self, tcx: &ty::ctxt<'tcx>, substs: &Substs<'tcx>)
17801784
-> GenericBounds<'tcx> {
17811785
GenericBounds {

src/librustc_typeck/check/_match.rs

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ use syntax::print::pprust;
3030
use syntax::ptr::P;
3131

3232
pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
33-
pat: &ast::Pat, expected: Ty<'tcx>) {
33+
pat: &ast::Pat,
34+
expected: Ty<'tcx>)
35+
{
3436
let fcx = pcx.fcx;
3537
let tcx = pcx.fcx.ccx.tcx;
3638

@@ -46,6 +48,19 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
4648
check_expr(fcx, &**lt);
4749
let expr_ty = fcx.expr_ty(&**lt);
4850
fcx.write_ty(pat.id, expr_ty);
51+
52+
// somewhat surprising: in this case, the subtyping
53+
// relation goes the opposite way as the other
54+
// cases. Actually what we really want is not a subtyping
55+
// relation at all but rather that there exists a LUB (so
56+
// that they can be compared). However, in practice,
57+
// constants are always scalars or strings. For scalars
58+
// subtyping is irrelevant, and for strings `expr_ty` is
59+
// type is `&'static str`, so if we say that
60+
//
61+
// &'static str <: expected
62+
//
63+
// that's equivalent to there existing a LUB.
4964
demand::suptype(fcx, pat.span, expected, expr_ty);
5065
}
5166
ast::PatRange(ref begin, ref end) => {
@@ -54,10 +69,16 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
5469

5570
let lhs_ty = fcx.expr_ty(&**begin);
5671
let rhs_ty = fcx.expr_ty(&**end);
57-
if require_same_types(
58-
tcx, Some(fcx.infcx()), false, pat.span, lhs_ty, rhs_ty,
59-
|| "mismatched types in range".to_string())
60-
&& (ty::type_is_numeric(lhs_ty) || ty::type_is_char(rhs_ty)) {
72+
73+
let lhs_eq_rhs =
74+
require_same_types(
75+
tcx, Some(fcx.infcx()), false, pat.span, lhs_ty, rhs_ty,
76+
|| "mismatched types in range".to_string());
77+
78+
let numeric_or_char =
79+
lhs_eq_rhs && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(lhs_ty));
80+
81+
if numeric_or_char {
6182
match valid_range_bounds(fcx.ccx, &**begin, &**end) {
6283
Some(false) => {
6384
span_err!(tcx.sess, begin.span, E0030,
@@ -71,38 +92,59 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
7192
}
7293
} else {
7394
span_err!(tcx.sess, begin.span, E0029,
74-
"only char and numeric types are allowed in range");
95+
"only char and numeric types are allowed in range");
7596
}
7697

7798
fcx.write_ty(pat.id, lhs_ty);
99+
100+
// subtyping doens't matter here, as the value is some kind of scalar
78101
demand::eqtype(fcx, pat.span, expected, lhs_ty);
79102
}
80103
ast::PatEnum(..) | ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => {
81104
let const_did = tcx.def_map.borrow()[pat.id].clone().def_id();
82105
let const_scheme = ty::lookup_item_type(tcx, const_did);
83-
fcx.write_ty(pat.id, const_scheme.ty);
84-
demand::suptype(fcx, pat.span, expected, const_scheme.ty);
106+
assert!(const_scheme.generics.is_empty());
107+
let const_ty = pcx.fcx.instantiate_type_scheme(pat.span,
108+
&Substs::empty(),
109+
&const_scheme.ty);
110+
fcx.write_ty(pat.id, const_ty);
111+
112+
// FIXME(#20489) -- we should limit the types here to scalars or something!
113+
114+
// As with PatLit, what we really want here is that there
115+
// exist a LUB, but for the cases that can occur, subtype
116+
// is good enough.
117+
demand::suptype(fcx, pat.span, expected, const_ty);
85118
}
86119
ast::PatIdent(bm, ref path, ref sub) if pat_is_binding(&tcx.def_map, pat) => {
87120
let typ = fcx.local_ty(pat.span, pat.id);
88121
match bm {
89122
ast::BindByRef(mutbl) => {
90123
// if the binding is like
91124
// ref x | ref const x | ref mut x
92-
// then the type of x is &M T where M is the mutability
93-
// and T is the expected type
125+
// then `x` is assigned a value of type `&M T` where M is the mutability
126+
// and T is the expected type.
94127
let region_var = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
95128
let mt = ty::mt { ty: expected, mutbl: mutbl };
96129
let region_ty = ty::mk_rptr(tcx, tcx.mk_region(region_var), mt);
130+
131+
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
132+
// required. However, we use equality, which is stronger. See (*) for
133+
// an explanation.
97134
demand::eqtype(fcx, pat.span, region_ty, typ);
98135
}
99136
// otherwise the type of x is the expected type T
100137
ast::BindByValue(_) => {
138+
// As above, `T <: typeof(x)` is required but we
139+
// use equality, see (*) below.
101140
demand::eqtype(fcx, pat.span, expected, typ);
102141
}
103142
}
143+
104144
fcx.write_ty(pat.id, typ);
105145

146+
// if there are multiple arms, make sure they all agree on
147+
// what the type of the binding `x` ought to be
106148
let canon_id = pcx.map[path.node];
107149
if canon_id != pat.id {
108150
let ct = fcx.local_ty(pat.span, canon_id);
@@ -124,8 +166,9 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
124166
check_pat_struct(pcx, pat, path, fields.as_slice(), etc, expected);
125167
}
126168
ast::PatTup(ref elements) => {
127-
let element_tys: Vec<_> = range(0, elements.len()).map(|_| fcx.infcx()
128-
.next_ty_var()).collect();
169+
let element_tys: Vec<_> =
170+
range(0, elements.len()).map(|_| fcx.infcx().next_ty_var())
171+
.collect();
129172
let pat_ty = ty::mk_tup(tcx, element_tys.clone());
130173
fcx.write_ty(pat.id, pat_ty);
131174
demand::eqtype(fcx, pat.span, expected, pat_ty);
@@ -138,7 +181,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
138181
let uniq_ty = ty::mk_uniq(tcx, inner_ty);
139182

140183
if check_dereferencable(pcx, pat.span, expected, &**inner) {
141-
demand::suptype(fcx, pat.span, expected, uniq_ty);
184+
// Here, `demand::subtype` is good enough, but I don't
185+
// think any errors can be introduced by using
186+
// `demand::eqtype`.
187+
demand::eqtype(fcx, pat.span, expected, uniq_ty);
142188
fcx.write_ty(pat.id, uniq_ty);
143189
check_pat(pcx, &**inner, inner_ty);
144190
} else {
@@ -150,15 +196,18 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
150196
let inner_ty = fcx.infcx().next_ty_var();
151197

152198
let mutbl =
153-
ty::deref(fcx.infcx().shallow_resolve(expected), true)
154-
.map_or(ast::MutImmutable, |mt| mt.mutbl);
199+
ty::deref(fcx.infcx().shallow_resolve(expected), true).map(|mt| mt.mutbl)
200+
.unwrap_or(ast::MutImmutable);
155201

156202
let mt = ty::mt { ty: inner_ty, mutbl: mutbl };
157203
let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
158204
let rptr_ty = ty::mk_rptr(tcx, tcx.mk_region(region), mt);
159205

160206
if check_dereferencable(pcx, pat.span, expected, &**inner) {
161-
demand::suptype(fcx, pat.span, expected, rptr_ty);
207+
// `demand::subtype` would be good enough, but using
208+
// `eqtype` turns out to be equally general. See (*)
209+
// below for details.
210+
demand::eqtype(fcx, pat.span, expected, rptr_ty);
162211
fcx.write_ty(pat.id, rptr_ty);
163212
check_pat(pcx, &**inner, inner_ty);
164213
} else {
@@ -181,14 +230,18 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
181230
let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
182231
ty::mk_slice(tcx, tcx.mk_region(region), ty::mt {
183232
ty: inner_ty,
184-
mutbl: ty::deref(expected_ty, true)
185-
.map_or(ast::MutImmutable, |mt| mt.mutbl)
233+
mutbl: ty::deref(expected_ty, true).map(|mt| mt.mutbl)
234+
.unwrap_or(ast::MutImmutable)
186235
})
187236
}
188237
};
189238

190239
fcx.write_ty(pat.id, pat_ty);
191-
demand::suptype(fcx, pat.span, expected, pat_ty);
240+
241+
// `demand::subtype` would be good enough, but using
242+
// `eqtype` turns out to be equally general. See (*)
243+
// below for details.
244+
demand::eqtype(fcx, pat.span, expected, pat_ty);
192245

193246
for elt in before.iter() {
194247
check_pat(pcx, &**elt, inner_ty);
@@ -210,6 +263,56 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
210263
}
211264
ast::PatMac(_) => tcx.sess.bug("unexpanded macro")
212265
}
266+
267+
268+
// (*) In most of the cases above (literals and constants being
269+
// the exception), we relate types using strict equality, evewn
270+
// though subtyping would be sufficient. There are a few reasons
271+
// for this, some of which are fairly subtle and which cost me
272+
// (nmatsakis) an hour or two debugging to remember, so I thought
273+
// I'd write them down this time.
274+
//
275+
// 1. Most importantly, there is no loss of expressiveness
276+
// here. What we are saying is that the type of `x`
277+
// becomes *exactly* what is expected. This might seem
278+
// like it will cause errors in a case like this:
279+
//
280+
// ```
281+
// fn foo<'x>(x: &'x int) {
282+
// let a = 1;
283+
// let mut z = x;
284+
// z = &a;
285+
// }
286+
// ```
287+
//
288+
// The reason we might get an error is that `z` might be
289+
// assigned a type like `&'x int`, and then we would have
290+
// a problem when we try to assign `&a` to `z`, because
291+
// the lifetime of `&a` (i.e., the enclosing block) is
292+
// shorter than `'x`.
293+
//
294+
// HOWEVER, this code works fine. The reason is that the
295+
// expected type here is whatever type the user wrote, not
296+
// the initializer's type. In this case the user wrote
297+
// nothing, so we are going to create a type variable `Z`.
298+
// Then we will assign the type of the initializer (`&'x
299+
// int`) as a subtype of `Z`: `&'x int <: Z`. And hence we
300+
// will instantiate `Z` as a type `&'0 int` where `'0` is
301+
// a fresh region variable, with the constraint that `'x :
302+
// '0`. So basically we're all set.
303+
//
304+
// Note that there are two tests to check that this remains true
305+
// (`regions-reassign-{match,let}-bound-pointer.rs`).
306+
//
307+
// 2. Things go horribly wrong if we use subtype. The reason for
308+
// THIS is a fairly subtle case involving bound regions. See the
309+
// `givens` field in `region_inference`, as well as the test
310+
// `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
311+
// for details. Short version is that we must sometimes detect
312+
// relationships between specific region variables and regions
313+
// bound in a closure signature, and that detection gets thrown
314+
// off when we substitute fresh region variables here to enable
315+
// subtyping.
213316
}
214317

215318
pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,

src/librustc_typeck/check/demand.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ use syntax::ast;
1818
use syntax::codemap::Span;
1919
use util::ppaux::Repr;
2020

21-
// Requires that the two types unify, and prints an error message if they
22-
// don't.
21+
// Requires that the two types unify, and prints an error message if
22+
// they don't.
2323
pub fn suptype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
24-
expected: Ty<'tcx>, actual: Ty<'tcx>) {
25-
suptype_with_fn(fcx, sp, false, expected, actual,
24+
ty_expected: Ty<'tcx>, ty_actual: Ty<'tcx>) {
25+
suptype_with_fn(fcx, sp, false, ty_expected, ty_actual,
2626
|sp, e, a, s| { fcx.report_mismatched_types(sp, e, a, s) })
2727
}
2828

29+
/// As `suptype`, but call `handle_err` if unification for subtyping fails.
2930
pub fn suptype_with_fn<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
3031
sp: Span,
3132
b_is_expected: bool,
@@ -48,9 +49,7 @@ pub fn eqtype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
4849
expected: Ty<'tcx>, actual: Ty<'tcx>) {
4950
match infer::mk_eqty(fcx.infcx(), false, infer::Misc(sp), actual, expected) {
5051
Ok(()) => { /* ok */ }
51-
Err(ref err) => {
52-
fcx.report_mismatched_types(sp, expected, actual, err);
53-
}
52+
Err(ref err) => { fcx.report_mismatched_types(sp, expected, actual, err); }
5453
}
5554
}
5655

src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4554,7 +4554,7 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
45544554
pub fn check_decl_initializer(fcx: &FnCtxt,
45554555
nid: ast::NodeId,
45564556
init: &ast::Expr)
4557-
{
4557+
{
45584558
let local_ty = fcx.local_ty(init.span, nid);
45594559
check_expr_coercable_to_type(fcx, init, local_ty)
45604560
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn assert_send<T: Send>(_t: T) {}
12+
13+
fn main() {
14+
let line = String::new();
15+
match [line.as_slice()] { //~ ERROR `line` does not live long enough
16+
[ word ] => { assert_send(word); }
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let a0 = 0u8;
13+
let f = 1u8;
14+
let mut a1 = &a0;
15+
match (&a1,) {
16+
(&ref b0,) => {
17+
a1 = &f; //~ ERROR cannot assign
18+
}
19+
}
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that the type checker permits us to reassign `z` which
12+
// started out with a longer lifetime and was reassigned to a shorter
13+
// one (it should infer to be the intersection).
14+
15+
fn foo(x: &int) {
16+
let a = 1;
17+
let mut z = x;
18+
z = &a;
19+
}
20+
21+
pub fn main() {
22+
foo(&1);
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that the type checker permits us to reassign `z` which
12+
// started out with a longer lifetime and was reassigned to a shorter
13+
// one (it should infer to be the intersection).
14+
15+
fn foo(x: &int) {
16+
let a = 1;
17+
match x {
18+
mut z => {
19+
z = &a;
20+
}
21+
}
22+
}
23+
24+
pub fn main() {
25+
foo(&1);
26+
}

0 commit comments

Comments
 (0)