Skip to content

Commit 287a544

Browse files
committed
Auto merge of #24158 - sanxiyn:cast, r=nrc
Fix #13993. Fix #17167.
2 parents 6436e34 + e2ff188 commit 287a544

File tree

4 files changed

+224
-154
lines changed

4 files changed

+224
-154
lines changed

src/librustc_typeck/check/cast.rs

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Copyright 2015 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+
//! Code for type-checking cast expressions.
12+
13+
use super::coercion;
14+
use super::demand;
15+
use super::FnCtxt;
16+
use super::structurally_resolved_type;
17+
18+
use lint;
19+
use middle::infer;
20+
use middle::ty;
21+
use middle::ty::Ty;
22+
use syntax::ast;
23+
use syntax::codemap::Span;
24+
25+
/// Reifies a cast check to be checked once we have full type information for
26+
/// a function context.
27+
pub struct CastCheck<'tcx> {
28+
expr: ast::Expr,
29+
expr_ty: Ty<'tcx>,
30+
cast_ty: Ty<'tcx>,
31+
span: Span,
32+
}
33+
34+
impl<'tcx> CastCheck<'tcx> {
35+
pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
36+
-> CastCheck<'tcx> {
37+
CastCheck {
38+
expr: expr,
39+
expr_ty: expr_ty,
40+
cast_ty: cast_ty,
41+
span: span,
42+
}
43+
}
44+
}
45+
46+
pub fn check_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, cast: &CastCheck<'tcx>) {
47+
fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
48+
span: Span,
49+
t_1: Ty<'tcx>,
50+
t_e: Ty<'tcx>) {
51+
fcx.type_error_message(span, |actual| {
52+
format!("illegal cast; cast through an \
53+
integer first: `{}` as `{}`",
54+
actual,
55+
fcx.infcx().ty_to_string(t_1))
56+
}, t_e, None);
57+
}
58+
59+
let span = cast.span;
60+
let e = &cast.expr;
61+
let t_e = structurally_resolved_type(fcx, span, cast.expr_ty);
62+
let t_1 = structurally_resolved_type(fcx, span, cast.cast_ty);
63+
64+
// Check for trivial casts.
65+
if !ty::type_has_ty_infer(t_1) {
66+
if let Ok(()) = coercion::mk_assignty(fcx, e, t_e, t_1) {
67+
if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) {
68+
fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS,
69+
e.id,
70+
span,
71+
format!("trivial numeric cast: `{}` as `{}`. Cast can be \
72+
replaced by coercion, this might require type \
73+
ascription or a temporary variable",
74+
fcx.infcx().ty_to_string(t_e),
75+
fcx.infcx().ty_to_string(t_1)));
76+
} else {
77+
fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_CASTS,
78+
e.id,
79+
span,
80+
format!("trivial cast: `{}` as `{}`. Cast can be \
81+
replaced by coercion, this might require type \
82+
ascription or a temporary variable",
83+
fcx.infcx().ty_to_string(t_e),
84+
fcx.infcx().ty_to_string(t_1)));
85+
}
86+
return;
87+
}
88+
}
89+
90+
let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e);
91+
let t_e_is_scalar = ty::type_is_scalar(t_e);
92+
let t_e_is_integral = ty::type_is_integral(t_e);
93+
let t_e_is_float = ty::type_is_floating_point(t_e);
94+
let t_e_is_c_enum = ty::type_is_c_like_enum(fcx.tcx(), t_e);
95+
96+
let t_1_is_scalar = ty::type_is_scalar(t_1);
97+
let t_1_is_integral = ty::type_is_integral(t_1);
98+
let t_1_is_char = ty::type_is_char(t_1);
99+
let t_1_is_bare_fn = ty::type_is_bare_fn(t_1);
100+
let t_1_is_float = ty::type_is_floating_point(t_1);
101+
let t_1_is_c_enum = ty::type_is_c_like_enum(fcx.tcx(), t_1);
102+
103+
// casts to scalars other than `char` and `bare fn` are trivial
104+
let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn;
105+
106+
if t_e_is_bare_fn_item && t_1_is_bare_fn {
107+
demand::coerce(fcx, e.span, t_1, &e);
108+
} else if t_1_is_char {
109+
let t_e = fcx.infcx().shallow_resolve(t_e);
110+
if t_e.sty != ty::ty_uint(ast::TyU8) {
111+
fcx.type_error_message(span, |actual| {
112+
format!("only `u8` can be cast as `char`, not `{}`", actual)
113+
}, t_e, None);
114+
}
115+
} else if t_1.sty == ty::ty_bool {
116+
span_err!(fcx.tcx().sess, span, E0054,
117+
"cannot cast as `bool`, compare with zero instead");
118+
} else if t_e_is_float && (t_1_is_scalar || t_1_is_c_enum) &&
119+
!(t_1_is_integral || t_1_is_float) {
120+
// Casts from float must go through an integer
121+
cast_through_integer_err(fcx, span, t_1, t_e)
122+
} else if t_1_is_float && (t_e_is_scalar || t_e_is_c_enum) &&
123+
!(t_e_is_integral || t_e_is_float || t_e.sty == ty::ty_bool) {
124+
// Casts to float must go through an integer or boolean
125+
cast_through_integer_err(fcx, span, t_1, t_e)
126+
} else if t_e_is_c_enum && t_1_is_trivial {
127+
if ty::type_is_unsafe_ptr(t_1) {
128+
// ... and likewise with C enum -> *T
129+
cast_through_integer_err(fcx, span, t_1, t_e)
130+
}
131+
// casts from C-like enums are allowed
132+
} else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
133+
fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
134+
t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool {
135+
match t1.sty {
136+
ty::ty_vec(_, Some(_)) => {}
137+
_ => return false
138+
}
139+
if ty::type_needs_infer(t2) {
140+
// This prevents this special case from going off when casting
141+
// to a type that isn't fully specified; e.g. `as *_`. (Issue
142+
// #14893.)
143+
return false
144+
}
145+
146+
let el = ty::sequence_element_type(fcx.tcx(), t1);
147+
infer::mk_eqty(fcx.infcx(),
148+
false,
149+
infer::Misc(sp),
150+
el,
151+
t2).is_ok()
152+
}
153+
154+
// Due to the limitations of LLVM global constants,
155+
// region pointers end up pointing at copies of
156+
// vector elements instead of the original values.
157+
// To allow unsafe pointers to work correctly, we
158+
// need to special-case obtaining an unsafe pointer
159+
// from a region pointer to a vector.
160+
161+
/* this cast is only allowed from &[T, ..n] to *T or
162+
&T to *T. */
163+
match (&t_e.sty, &t_1.sty) {
164+
(&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
165+
&ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
166+
if types_compatible(fcx, e.span, mt1, mt2) => {
167+
/* this case is allowed */
168+
}
169+
_ => {
170+
demand::coerce(fcx, e.span, t_1, &e);
171+
}
172+
}
173+
} else if fcx.type_is_fat_ptr(t_e, span) && !fcx.type_is_fat_ptr(t_1, span) {
174+
fcx.type_error_message(span, |actual| {
175+
format!("illegal cast; cast from fat pointer: `{}` as `{}`",
176+
actual, fcx.infcx().ty_to_string(t_1))
177+
}, t_e, None);
178+
} else if !(t_e_is_scalar && t_1_is_trivial) {
179+
/*
180+
If more type combinations should be supported than are
181+
supported here, then file an enhancement issue and
182+
record the issue number in this comment.
183+
*/
184+
fcx.type_error_message(span, |actual| {
185+
format!("non-scalar cast: `{}` as `{}`",
186+
actual,
187+
fcx.infcx().ty_to_string(t_1))
188+
}, t_e, None);
189+
}
190+
}

0 commit comments

Comments
 (0)