Skip to content

Commit 9b820d0

Browse files
committed
Correct the subtyping relations created by the pattern typechecking
code. Previously we were creating a subtyping relation in the wrong direction. We now just unify types, which is stronger than necessary but turns out fine. Fixes #19552. Fixes #19997.
1 parent fe7e285 commit 9b820d0

6 files changed

+251
-9
lines changed

src/librustc_typeck/check/_match.rs

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

5568
let lhs_ty = fcx.expr_ty(&**begin);
5669
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)) {
70+
71+
let lhs_eq_rhs =
72+
require_same_types(
73+
tcx, Some(fcx.infcx()), false, pat.span, lhs_ty, rhs_ty,
74+
|| "mismatched types in range".to_string());
75+
76+
let numeric_or_char =
77+
lhs_eq_rhs && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(lhs_ty));
78+
79+
if numeric_or_char {
6180
match valid_range_bounds(fcx.ccx, &**begin, &**end) {
6281
Some(false) => {
6382
span_err!(tcx.sess, begin.span, E0030,
@@ -75,6 +94,8 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
7594
}
7695

7796
fcx.write_ty(pat.id, lhs_ty);
97+
98+
// subtyping doens't matter here, as the value is some kind of scalar
7899
demand::eqtype(fcx, pat.span, expected, lhs_ty);
79100
}
80101
ast::PatEnum(..) | ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => {
@@ -89,20 +110,29 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
89110
ast::BindByRef(mutbl) => {
90111
// if the binding is like
91112
// 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
113+
// then `x` is assigned a value of type `&M T` where M is the mutability
114+
// and T is the expected type.
94115
let region_var = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
95116
let mt = ty::mt { ty: expected, mutbl: mutbl };
96117
let region_ty = ty::mk_rptr(tcx, tcx.mk_region(region_var), mt);
118+
119+
// `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` is
120+
// required. However, we use equality, which is stronger. See (*) for
121+
// an explanation.
97122
demand::eqtype(fcx, pat.span, region_ty, typ);
98123
}
99124
// otherwise the type of x is the expected type T
100125
ast::BindByValue(_) => {
126+
// As above, `T <: typeof(x)` is required but we
127+
// use equality, see (*) below.
101128
demand::eqtype(fcx, pat.span, expected, typ);
102129
}
103130
}
131+
104132
fcx.write_ty(pat.id, typ);
105133

134+
// if there are multiple arms, make sure they all agree on
135+
// what the type of the binding `x` ought to be
106136
let canon_id = pcx.map[path.node];
107137
if canon_id != pat.id {
108138
let ct = fcx.local_ty(pat.span, canon_id);
@@ -138,7 +168,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
138168
let uniq_ty = ty::mk_uniq(tcx, inner_ty);
139169

140170
if check_dereferencable(pcx, pat.span, expected, &**inner) {
141-
demand::suptype(fcx, pat.span, expected, uniq_ty);
171+
// Here, `demand::subtype` is good enough, but I don't
172+
// think any errors can be introduced by using
173+
// `demand::eqtype`.
174+
demand::eqtype(fcx, pat.span, expected, uniq_ty);
142175
fcx.write_ty(pat.id, uniq_ty);
143176
check_pat(pcx, &**inner, inner_ty);
144177
} else {
@@ -158,7 +191,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
158191
let rptr_ty = ty::mk_rptr(tcx, tcx.mk_region(region), mt);
159192

160193
if check_dereferencable(pcx, pat.span, expected, &**inner) {
161-
demand::suptype(fcx, pat.span, expected, rptr_ty);
194+
// `demand::subtype` would be good enough, but using
195+
// `eqtype` turns out to be equally general. See (*)
196+
// below for details.
197+
demand::eqtype(fcx, pat.span, expected, rptr_ty);
162198
fcx.write_ty(pat.id, rptr_ty);
163199
check_pat(pcx, &**inner, inner_ty);
164200
} else {
@@ -188,7 +224,11 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
188224
};
189225

190226
fcx.write_ty(pat.id, pat_ty);
191-
demand::suptype(fcx, pat.span, expected, pat_ty);
227+
228+
// `demand::subtype` would be good enough, but using
229+
// `eqtype` turns out to be equally general. See (*)
230+
// below for details.
231+
demand::eqtype(fcx, pat.span, expected, pat_ty);
192232

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

215305
pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
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+
}
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+
}
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+
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
// Test that this fairly specialized, but also reasonable, pattern
12+
// typechecks. The pattern involves regions bound in closures that
13+
// wind up related to inference variables.
14+
//
15+
// NB. Changes to the region implementatiosn have broken this pattern
16+
// a few times, but it happens to be used in the compiler so those
17+
// changes were caught. However, those uses in the compiler could
18+
// easily get changed or refactored away in the future.
19+
20+
struct Ctxt<'tcx> {
21+
x: &'tcx Vec<int>
22+
}
23+
24+
struct Foo<'a,'tcx:'a> {
25+
cx: &'a Ctxt<'tcx>,
26+
}
27+
28+
impl<'a,'tcx> Foo<'a,'tcx> {
29+
fn bother(&mut self) -> int {
30+
self.elaborate_bounds(|this| {
31+
// (*) Here: type of `this` is `&'f0 Foo<&'f1, '_2>`,
32+
// where `'f0` and `'f1` are fresh, free regions that
33+
// result from the bound regions on the closure, and `'2`
34+
// is a region inference variable created by the call. Due
35+
// to the constraints on the type, we find that `'_2 : 'f1
36+
// + 'f2` must hold (and can be assumed by the callee).
37+
// Region inference has to do some clever stuff to avoid
38+
// inferring `'_2` to be `'static` in this case, because
39+
// it is created outside the closure but then related to
40+
// regions bound by the closure itself. See the
41+
// `region_inference.rs` file (and the `givens` field, in
42+
// particular) for more details.
43+
this.foo()
44+
})
45+
}
46+
47+
fn foo(&mut self) -> int {
48+
22
49+
}
50+
51+
fn elaborate_bounds(
52+
&mut self,
53+
mk_cand: for<'b>|this: &mut Foo<'b, 'tcx>| -> int)
54+
-> int
55+
{
56+
mk_cand(self)
57+
}
58+
}
59+
60+
fn main() {
61+
let v = vec!();
62+
let cx = Ctxt { x: &v };
63+
let mut foo = Foo { cx: &cx };
64+
assert_eq!(foo.bother(), 22); // just so the code is not dead, basically
65+
}

0 commit comments

Comments
 (0)