Skip to content

Commit 315f2a7

Browse files
committed
librustc: Don't try to perform the magical
vector-reference-to-unsafe-pointer-to-element cast if the type to be casted to is not fully specified. This is a conservative change to fix the user-visible symptoms of the issue. A more flexible treatment would delay cast checks to after function typechecking. This can break code that did: let x: *u8 = &([0, 0]) as *_; Change this code to: let x: *u8 = &([0, 0]) as *u8; Closes #14893. [breaking-change]
1 parent 7a93bee commit 315f2a7

File tree

2 files changed

+191
-129
lines changed

2 files changed

+191
-129
lines changed

src/librustc/middle/typeck/check/mod.rs

Lines changed: 160 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,164 @@ fn compare_impl_method(tcx: &ty::ctxt,
10601060
}
10611061
}
10621062

1063+
fn check_cast(fcx: &FnCtxt,
1064+
e: &ast::Expr,
1065+
t: &ast::Ty,
1066+
id: ast::NodeId,
1067+
span: Span) {
1068+
// Find the type of `e`. Supply hints based on the type we are casting to,
1069+
// if appropriate.
1070+
let t_1 = fcx.to_ty(t);
1071+
let t_1 = structurally_resolved_type(fcx, span, t_1);
1072+
1073+
if ty::type_is_scalar(t_1) {
1074+
// Supply the type as a hint so as to influence integer
1075+
// literals and other things that might care.
1076+
check_expr_with_hint(fcx, e, t_1)
1077+
} else {
1078+
check_expr(fcx, e)
1079+
}
1080+
1081+
let t_e = fcx.expr_ty(e);
1082+
1083+
debug!("t_1={}", fcx.infcx().ty_to_str(t_1));
1084+
debug!("t_e={}", fcx.infcx().ty_to_str(t_e));
1085+
1086+
if ty::type_is_error(t_e) {
1087+
fcx.write_error(id);
1088+
return
1089+
}
1090+
if ty::type_is_bot(t_e) {
1091+
fcx.write_bot(id);
1092+
return
1093+
}
1094+
1095+
if ty::type_is_trait(t_1) {
1096+
// This will be looked up later on.
1097+
fcx.write_ty(id, t_1);
1098+
return
1099+
}
1100+
1101+
let t_1 = structurally_resolved_type(fcx, span, t_1);
1102+
let t_e = structurally_resolved_type(fcx, span, t_e);
1103+
1104+
if ty::type_is_nil(t_e) {
1105+
fcx.type_error_message(span, |actual| {
1106+
format!("cast from nil: `{}` as `{}`",
1107+
actual,
1108+
fcx.infcx().ty_to_str(t_1))
1109+
}, t_e, None);
1110+
} else if ty::type_is_nil(t_1) {
1111+
fcx.type_error_message(span, |actual| {
1112+
format!("cast to nil: `{}` as `{}`",
1113+
actual,
1114+
fcx.infcx().ty_to_str(t_1))
1115+
}, t_e, None);
1116+
}
1117+
1118+
let t_1_is_scalar = ty::type_is_scalar(t_1);
1119+
let t_1_is_char = ty::type_is_char(t_1);
1120+
let t_1_is_bare_fn = ty::type_is_bare_fn(t_1);
1121+
let t_1_is_float = ty::type_is_floating_point(t_1);
1122+
1123+
// casts to scalars other than `char` and `bare fn` are trivial
1124+
let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn;
1125+
if ty::type_is_c_like_enum(fcx.tcx(), t_e) && t_1_is_trivial {
1126+
if t_1_is_float {
1127+
fcx.type_error_message(span, |actual| {
1128+
format!("illegal cast; cast through an \
1129+
integer first: `{}` as `{}`",
1130+
actual,
1131+
fcx.infcx().ty_to_str(t_1))
1132+
}, t_e, None);
1133+
}
1134+
// casts from C-like enums are allowed
1135+
} else if t_1_is_char {
1136+
let t_e = fcx.infcx().resolve_type_vars_if_possible(t_e);
1137+
if ty::get(t_e).sty != ty::ty_uint(ast::TyU8) {
1138+
fcx.type_error_message(span, |actual| {
1139+
format!("only `u8` can be cast as \
1140+
`char`, not `{}`", actual)
1141+
}, t_e, None);
1142+
}
1143+
} else if ty::get(t_1).sty == ty::ty_bool {
1144+
fcx.tcx()
1145+
.sess
1146+
.span_err(span,
1147+
"cannot cast as `bool`, compare with zero instead");
1148+
} else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
1149+
fn is_vec(t: ty::t) -> bool {
1150+
match ty::get(t).sty {
1151+
ty::ty_vec(..) => true,
1152+
ty::ty_ptr(ty::mt{ty: t, ..}) |
1153+
ty::ty_rptr(_, ty::mt{ty: t, ..}) |
1154+
ty::ty_box(t) |
1155+
ty::ty_uniq(t) => {
1156+
match ty::get(t).sty {
1157+
ty::ty_vec(_, None) => true,
1158+
_ => false,
1159+
}
1160+
}
1161+
_ => false
1162+
}
1163+
}
1164+
fn types_compatible(fcx: &FnCtxt, sp: Span,
1165+
t1: ty::t, t2: ty::t) -> bool {
1166+
if !is_vec(t1) {
1167+
// If the type being casted from is not a vector, this special
1168+
// case does not apply.
1169+
return false
1170+
}
1171+
if ty::type_needs_infer(t2) {
1172+
// This prevents this special case from going off when casting
1173+
// to a type that isn't fully specified; e.g. `as *_`. (Issue
1174+
// #14893.)
1175+
return false
1176+
}
1177+
1178+
let el = ty::sequence_element_type(fcx.tcx(), t1);
1179+
infer::mk_eqty(fcx.infcx(),
1180+
false,
1181+
infer::Misc(sp),
1182+
el,
1183+
t2).is_ok()
1184+
}
1185+
1186+
// Due to the limitations of LLVM global constants,
1187+
// region pointers end up pointing at copies of
1188+
// vector elements instead of the original values.
1189+
// To allow unsafe pointers to work correctly, we
1190+
// need to special-case obtaining an unsafe pointer
1191+
// from a region pointer to a vector.
1192+
1193+
/* this cast is only allowed from &[T] to *T or
1194+
&T to *T. */
1195+
match (&ty::get(t_e).sty, &ty::get(t_1).sty) {
1196+
(&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
1197+
&ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
1198+
if types_compatible(fcx, e.span, mt1, mt2) => {
1199+
/* this case is allowed */
1200+
}
1201+
_ => {
1202+
demand::coerce(fcx, e.span, t_1, &*e);
1203+
}
1204+
}
1205+
} else if !(ty::type_is_scalar(t_e) && t_1_is_trivial) {
1206+
/*
1207+
If more type combinations should be supported than are
1208+
supported here, then file an enhancement issue and
1209+
record the issue number in this comment.
1210+
*/
1211+
fcx.type_error_message(span, |actual| {
1212+
format!("non-scalar cast: `{}` as `{}`",
1213+
actual,
1214+
fcx.infcx().ty_to_str(t_1))
1215+
}, t_e, None);
1216+
}
1217+
1218+
fcx.write_ty(id, t_1);
1219+
}
1220+
10631221
impl<'a> AstConv for FnCtxt<'a> {
10641222
fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.ccx.tcx }
10651223

@@ -3049,11 +3207,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
30493207
fcx.write_bot(id);
30503208
}
30513209
}
3052-
ast::ExprCast(expr_from, t) => {
3053-
let ty_to = fcx.to_ty(t);
3054-
debug!("ExprCast ty_to={}", fcx.infcx().ty_to_str(ty_to));
3055-
check_cast(fcx, expr_from, ty_to);
3056-
fcx.write_ty(id, ty_to);
3210+
ast::ExprCast(ref e, ref t) => {
3211+
check_cast(fcx, &**e, &**t, id, expr.span);
30573212
}
30583213
ast::ExprVec(ref args) => {
30593214
let t: ty::t = fcx.infcx().next_ty_var();
@@ -3248,130 +3403,6 @@ impl Repr for Expectation {
32483403
}
32493404
}
32503405

3251-
fn check_cast(fcx: &FnCtxt, expr_from: Gc<ast::Expr>, ty_to: ty::t) {
3252-
// Find the type of `expr_from`. Supply hints based on the type
3253-
// we are casting to, if appropriate.
3254-
let ty_to = structurally_resolved_type(fcx, expr_from.span, ty_to);
3255-
if ty::type_is_scalar(ty_to) {
3256-
// Supply the type as a hint so as to influence integer
3257-
// literals and other things that might care.
3258-
check_expr_with_hint(fcx, expr_from, ty_to)
3259-
} else {
3260-
check_expr(fcx, expr_from)
3261-
}
3262-
let ty_from = fcx.expr_ty(expr_from);
3263-
3264-
// Object creation is checked during the vtable phase.
3265-
if ty::type_is_trait(ty_to) {
3266-
check_expr(fcx, expr_from);
3267-
return;
3268-
}
3269-
3270-
let ty_from = fcx.infcx().resolve_type_vars_if_possible(ty_from);
3271-
3272-
if ty::type_is_nil(ty_from) {
3273-
fcx.type_error_message(expr_from.span, |actual| {
3274-
format!("cast from nil: `{}` as `{}`", actual,
3275-
fcx.infcx().ty_to_str(ty_to))
3276-
}, ty_from, None);
3277-
return;
3278-
}
3279-
3280-
if ty::type_is_nil(ty_to) {
3281-
fcx.type_error_message(expr_from.span, |actual| {
3282-
format!("cast to nil: `{}` as `{}`", actual,
3283-
fcx.infcx().ty_to_str(ty_to))
3284-
}, ty_from, None);
3285-
return;
3286-
}
3287-
3288-
let t_e = structurally_resolved_type(fcx, expr_from.span, ty_from);
3289-
let t_1 = structurally_resolved_type(fcx, expr_from.span, ty_to);
3290-
3291-
let to_is_scalar = ty::type_is_scalar(t_1);
3292-
let to_is_float = ty::type_is_floating_point(t_1);
3293-
let to_is_char = ty::type_is_char(t_1);
3294-
let to_is_bare_fn = ty::type_is_bare_fn(t_1);
3295-
3296-
// casts to scalars other than `char` and `bare fn` are trivial
3297-
let to_is_trivial = to_is_scalar &&
3298-
!to_is_char && !to_is_bare_fn;
3299-
3300-
if ty::type_is_c_like_enum(fcx.tcx(), t_e) && to_is_trivial {
3301-
if to_is_float {
3302-
fcx.type_error_message(expr_from.span, |actual| {
3303-
format!("illegal cast; cast through an integer first: `{}` \
3304-
as `{}`",
3305-
actual,
3306-
fcx.infcx().ty_to_str(t_1))
3307-
}, ty_from, None);
3308-
}
3309-
// casts from C-like enums are allowed
3310-
} else if to_is_char {
3311-
if ty::get(ty_from).sty != ty::ty_uint(ast::TyU8) {
3312-
fcx.type_error_message(expr_from.span, |actual| {
3313-
format!("only `u8` can be cast as `char`, not `{}`", actual)
3314-
}, ty_from, None);
3315-
}
3316-
} else if ty::type_is_bool(t_1) {
3317-
fcx.tcx().sess.span_err(expr_from.span,
3318-
"cannot cast as `bool`, compare with zero instead");
3319-
} else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
3320-
fn is_vec(t: ty::t) -> bool {
3321-
match ty::get(t).sty {
3322-
ty::ty_vec(..) => true,
3323-
ty::ty_ptr(ty::mt{ty: t, ..}) |
3324-
ty::ty_rptr(_, ty::mt{ty: t, ..}) |
3325-
ty::ty_box(t) |
3326-
ty::ty_uniq(t) => match ty::get(t).sty {
3327-
ty::ty_vec(_, None) => true,
3328-
_ => false,
3329-
},
3330-
_ => false
3331-
}
3332-
}
3333-
fn types_compatible(fcx: &FnCtxt, sp: Span,
3334-
t1: ty::t, t2: ty::t) -> bool {
3335-
if !is_vec(t1) {
3336-
false
3337-
} else {
3338-
let el = ty::sequence_element_type(fcx.tcx(),
3339-
t1);
3340-
infer::mk_eqty(fcx.infcx(), false,
3341-
infer::Misc(sp), el, t2).is_ok()
3342-
}
3343-
}
3344-
3345-
// Due to the limitations of LLVM global constants,
3346-
// region pointers end up pointing at copies of
3347-
// vector elements instead of the original values.
3348-
// To allow unsafe pointers to work correctly, we
3349-
// need to special-case obtaining an unsafe pointer
3350-
// from a region pointer to a vector.
3351-
3352-
/* this cast is only allowed from &[T] to *T or
3353-
&T to *T. */
3354-
match (&ty::get(t_e).sty, &ty::get(t_1).sty) {
3355-
(&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
3356-
&ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
3357-
if types_compatible(fcx, expr_from.span, mt1, mt2) => {
3358-
/* this case is allowed */
3359-
}
3360-
_ => {
3361-
demand::coerce(fcx, expr_from.span, ty_to, expr_from);
3362-
}
3363-
}
3364-
} else if !(ty::type_is_scalar(t_e) && to_is_trivial) {
3365-
// If more type combinations should be supported than are
3366-
// supported here, then file an enhancement issue and
3367-
// record the issue number in this comment.
3368-
fcx.type_error_message(expr_from.span, |actual| {
3369-
format!("non-scalar cast: `{}` as `{}`", actual,
3370-
fcx.infcx().ty_to_str(ty_to))
3371-
}, ty_from, None);
3372-
}
3373-
}
3374-
33753406
pub fn require_uint(fcx: &FnCtxt, sp: Span, t: ty::t) {
33763407
if !type_is_uint(fcx, sp, t) {
33773408
fcx.type_error_message(sp, |actual| {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2012 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+
// Issue #14893. Tests that casts from vectors don't behave strangely in the
12+
// presence of the `_` type shorthand notation.
13+
14+
struct X {
15+
y: [u8, ..2],
16+
}
17+
18+
fn main() {
19+
let x1 = X { y: [0, 0] };
20+
21+
let p1: *u8 = &x1.y as *_; //~ ERROR mismatched types
22+
let t1: *[u8, ..2] = &x1.y as *_;
23+
let h1: *[u8, ..2] = &x1.y as *[u8, ..2];
24+
25+
let mut x1 = X { y: [0, 0] };
26+
27+
let p1: *mut u8 = &mut x1.y as *mut _; //~ ERROR mismatched types
28+
let t1: *mut [u8, ..2] = &mut x1.y as *mut _;
29+
let h1: *mut [u8, ..2] = &mut x1.y as *mut [u8, ..2];
30+
}
31+

0 commit comments

Comments
 (0)