@@ -76,6 +76,7 @@ use rustc::ty::relate::RelateResult;
76
76
use rustc:: ty:: subst:: Subst ;
77
77
use syntax:: abi;
78
78
use syntax:: feature_gate;
79
+ use syntax:: ptr:: P ;
79
80
80
81
use std:: collections:: VecDeque ;
81
82
use std:: ops:: Deref ;
@@ -155,11 +156,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
155
156
} )
156
157
}
157
158
158
- fn coerce < ' a , E , I > ( & self , exprs : & E , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> CoerceResult < ' tcx >
159
- where E : Fn ( ) -> I ,
160
- I : IntoIterator < Item = & ' a hir:: Expr >
159
+ fn coerce < E > ( & self , exprs : & [ E ] , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> CoerceResult < ' tcx >
160
+ where E : AsCoercionSite
161
161
{
162
-
163
162
let a = self . shallow_resolve ( a) ;
164
163
debug ! ( "Coerce.tys({:?} => {:?})" , a, b) ;
165
164
@@ -239,15 +238,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
239
238
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
240
239
/// To match `A` with `B`, autoderef will be performed,
241
240
/// calling `deref`/`deref_mut` where necessary.
242
- fn coerce_borrowed_pointer < ' a , E , I > ( & self ,
243
- exprs : & E ,
244
- a : Ty < ' tcx > ,
245
- b : Ty < ' tcx > ,
246
- r_b : & ' tcx ty:: Region ,
247
- mt_b : TypeAndMut < ' tcx > )
248
- -> CoerceResult < ' tcx >
249
- where E : Fn ( ) -> I ,
250
- I : IntoIterator < Item = & ' a hir:: Expr >
241
+ fn coerce_borrowed_pointer < E > ( & self ,
242
+ exprs : & [ E ] ,
243
+ a : Ty < ' tcx > ,
244
+ b : Ty < ' tcx > ,
245
+ r_b : & ' tcx ty:: Region ,
246
+ mt_b : TypeAndMut < ' tcx > )
247
+ -> CoerceResult < ' tcx >
248
+ where E : AsCoercionSite
251
249
{
252
250
253
251
debug ! ( "coerce_borrowed_pointer(a={:?}, b={:?})" , a, b) ;
@@ -424,7 +422,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
424
422
autoref) ;
425
423
426
424
let pref = LvaluePreference :: from_mutbl ( mt_b. mutbl ) ;
427
- obligations. extend ( autoderef. finalize_as_infer_ok ( pref, exprs ( ) ) . obligations ) ;
425
+ obligations. extend ( autoderef. finalize_as_infer_ok ( pref, exprs) . obligations ) ;
428
426
429
427
success ( Adjust :: DerefRef {
430
428
autoderefs : autoderefs,
@@ -699,7 +697,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
699
697
let cause = self . cause ( expr. span , ObligationCauseCode :: ExprAssignable ) ;
700
698
let coerce = Coerce :: new ( self , cause) ;
701
699
self . commit_if_ok ( |_| {
702
- let ok = coerce. coerce ( & || Some ( expr) , source, target) ?;
700
+ let ok = coerce. coerce ( & [ expr] , source, target) ?;
703
701
let adjustment = self . register_infer_ok_obligations ( ok) ;
704
702
if !adjustment. is_identity ( ) {
705
703
debug ! ( "Success, coerced with {:?}" , adjustment) ;
@@ -718,15 +716,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
718
716
///
719
717
/// This is really an internal helper. From outside the coercion
720
718
/// module, you should instantiate a `CoerceMany` instance.
721
- fn try_find_coercion_lub < ' b , E , I > ( & self ,
722
- cause : & ObligationCause < ' tcx > ,
723
- exprs : E ,
724
- prev_ty : Ty < ' tcx > ,
725
- new : & ' b hir:: Expr ,
726
- new_ty : Ty < ' tcx > )
727
- -> RelateResult < ' tcx , Ty < ' tcx > >
728
- where E : Fn ( ) -> I ,
729
- I : IntoIterator < Item = & ' b hir:: Expr >
719
+ fn try_find_coercion_lub < E > ( & self ,
720
+ cause : & ObligationCause < ' tcx > ,
721
+ exprs : & [ E ] ,
722
+ prev_ty : Ty < ' tcx > ,
723
+ new : & hir:: Expr ,
724
+ new_ty : Ty < ' tcx > )
725
+ -> RelateResult < ' tcx , Ty < ' tcx > >
726
+ where E : AsCoercionSite
730
727
{
731
728
732
729
let prev_ty = self . resolve_type_vars_with_obligations ( prev_ty) ;
@@ -758,7 +755,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
758
755
759
756
// Reify both sides and return the reified fn pointer type.
760
757
let fn_ptr = self . tcx . mk_fn_ptr ( fty) ;
761
- for expr in exprs ( ) . into_iter ( ) . chain ( Some ( new) ) {
758
+ for expr in exprs. iter ( ) . map ( |e| e . as_coercion_site ( ) ) . chain ( Some ( new) ) {
762
759
// No adjustments can produce a fn item, so this should never trip.
763
760
assert ! ( !self . tables. borrow( ) . adjustments. contains_key( & expr. id) ) ;
764
761
self . write_adjustment ( expr. id , Adjustment {
@@ -778,7 +775,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
778
775
// but only if the new expression has no coercion already applied to it.
779
776
let mut first_error = None ;
780
777
if !self . tables . borrow ( ) . adjustments . contains_key ( & new. id ) {
781
- let result = self . commit_if_ok ( |_| coerce. coerce ( & || Some ( new) , new_ty, prev_ty) ) ;
778
+ let result = self . commit_if_ok ( |_| coerce. coerce ( & [ new] , new_ty, prev_ty) ) ;
782
779
match result {
783
780
Ok ( ok) => {
784
781
let adjustment = self . register_infer_ok_obligations ( ok) ;
@@ -794,7 +791,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
794
791
// Then try to coerce the previous expressions to the type of the new one.
795
792
// This requires ensuring there are no coercions applied to *any* of the
796
793
// previous expressions, other than noop reborrows (ignoring lifetimes).
797
- for expr in exprs ( ) {
794
+ for expr in exprs {
795
+ let expr = expr. as_coercion_site ( ) ;
798
796
let noop = match self . tables . borrow ( ) . adjustments . get ( & expr. id ) . map ( |adj| adj. kind ) {
799
797
Some ( Adjust :: DerefRef {
800
798
autoderefs : 1 ,
@@ -838,7 +836,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
838
836
let adjustment = self . register_infer_ok_obligations ( ok) ;
839
837
if !adjustment. is_identity ( ) {
840
838
let mut tables = self . tables . borrow_mut ( ) ;
841
- for expr in exprs ( ) {
839
+ for expr in exprs {
840
+ let expr = expr. as_coercion_site ( ) ;
842
841
if let Some ( & mut Adjustment {
843
842
kind : Adjust :: NeverToAny ,
844
843
ref mut target
@@ -897,25 +896,61 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
897
896
/// let final_ty = coerce.complete(fcx);
898
897
/// ```
899
898
#[ derive( Clone ) ] // (*)
900
- pub struct CoerceMany < ' gcx : ' tcx , ' tcx > {
899
+ pub struct CoerceMany < ' gcx , ' tcx , ' exprs , E >
900
+ where ' gcx : ' tcx , E : ' exprs + AsCoercionSite ,
901
+ {
901
902
expected_ty : Ty < ' tcx > ,
902
903
final_ty : Option < Ty < ' tcx > > ,
903
- expressions : Vec < & ' gcx hir:: Expr > ,
904
+ expressions : Expressions < ' gcx , ' exprs , E > ,
905
+ pushed : usize ,
906
+ }
907
+
908
+ /// The type of a `CoerceMany` that is storing up the expressions into
909
+ /// a buffer. We use this in `check/mod.rs` for things like `break`.
910
+ pub type DynamicCoerceMany < ' gcx , ' tcx > = CoerceMany < ' gcx , ' tcx , ' static , hir:: Expr > ;
911
+
912
+ #[ derive( Clone ) ] // (*)
913
+ enum Expressions < ' gcx , ' exprs , E >
914
+ where E : ' exprs + AsCoercionSite ,
915
+ {
916
+ Dynamic ( Vec < & ' gcx hir:: Expr > ) ,
917
+ UpFront ( & ' exprs [ E ] ) ,
904
918
}
905
919
906
920
// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis
907
921
908
- impl < ' gcx , ' tcx > CoerceMany < ' gcx , ' tcx > {
922
+ impl < ' gcx , ' tcx , ' exprs , E > CoerceMany < ' gcx , ' tcx , ' exprs , E >
923
+ where ' gcx : ' tcx , E : ' exprs + AsCoercionSite ,
924
+ {
925
+ /// The usual case; collect the set of expressions dynamically.
926
+ /// If the full set of coercion sites is known before hand,
927
+ /// consider `with_coercion_sites()` instead to avoid allocation.
909
928
pub fn new ( expected_ty : Ty < ' tcx > ) -> Self {
929
+ Self :: make ( expected_ty, Expressions :: Dynamic ( vec ! [ ] ) )
930
+ }
931
+
932
+ /// As an optimization, you can create a `CoerceMany` with a
933
+ /// pre-existing slice of expressions. In this case, you are
934
+ /// expected to pass each element in the slice to `coerce(...)` in
935
+ /// order. This is used with arrays in particular to avoid
936
+ /// needlessly cloning the slice.
937
+ pub fn with_coercion_sites ( expected_ty : Ty < ' tcx > ,
938
+ coercion_sites : & ' exprs [ E ] )
939
+ -> Self {
940
+ Self :: make ( expected_ty, Expressions :: UpFront ( coercion_sites) )
941
+ }
942
+
943
+ fn make ( expected_ty : Ty < ' tcx > , expressions : Expressions < ' gcx , ' exprs , E > ) -> Self {
910
944
CoerceMany {
911
945
expected_ty,
912
946
final_ty : None ,
913
- expressions : vec ! [ ] ,
947
+ expressions,
948
+ pushed : 0 ,
914
949
}
915
950
}
916
951
917
952
pub fn is_empty ( & self ) -> bool {
918
- self . expressions . is_empty ( )
953
+ self . pushed == 0
919
954
}
920
955
921
956
/// Return the "expected type" with which this coercion was
@@ -997,16 +1032,25 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
997
1032
998
1033
// Handle the actual type unification etc.
999
1034
let result = if let Some ( expression) = expression {
1000
- if self . expressions . is_empty ( ) {
1035
+ if self . pushed == 0 {
1001
1036
// Special-case the first expression we are coercing.
1002
1037
// To be honest, I'm not entirely sure why we do this.
1003
1038
fcx. try_coerce ( expression, expression_ty, self . expected_ty )
1004
1039
} else {
1005
- fcx. try_find_coercion_lub ( cause,
1006
- || self . expressions . iter ( ) . cloned ( ) ,
1007
- self . merged_ty ( ) ,
1008
- expression,
1009
- expression_ty)
1040
+ match self . expressions {
1041
+ Expressions :: Dynamic ( ref exprs) =>
1042
+ fcx. try_find_coercion_lub ( cause,
1043
+ exprs,
1044
+ self . merged_ty ( ) ,
1045
+ expression,
1046
+ expression_ty) ,
1047
+ Expressions :: UpFront ( ref coercion_sites) =>
1048
+ fcx. try_find_coercion_lub ( cause,
1049
+ & coercion_sites[ 0 ..self . pushed ] ,
1050
+ self . merged_ty ( ) ,
1051
+ expression,
1052
+ expression_ty) ,
1053
+ }
1010
1054
}
1011
1055
} else {
1012
1056
// this is a hack for cases where we default to `()` because
@@ -1034,7 +1078,17 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
1034
1078
match result {
1035
1079
Ok ( v) => {
1036
1080
self . final_ty = Some ( v) ;
1037
- self . expressions . extend ( expression) ;
1081
+ if let Some ( e) = expression {
1082
+ match self . expressions {
1083
+ Expressions :: Dynamic ( ref mut buffer) => buffer. push ( e) ,
1084
+ Expressions :: UpFront ( coercion_sites) => {
1085
+ // if the user gave us an array to validate, check that we got
1086
+ // the next expression in the list, as expected
1087
+ assert_eq ! ( coercion_sites[ self . pushed] . as_coercion_site( ) . id, e. id) ;
1088
+ }
1089
+ }
1090
+ self . pushed += 1 ;
1091
+ }
1038
1092
}
1039
1093
Err ( err) => {
1040
1094
let ( expected, found) = if expression. is_none ( ) {
@@ -1076,8 +1130,46 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
1076
1130
} else {
1077
1131
// If we only had inputs that were of type `!` (or no
1078
1132
// inputs at all), then the final type is `!`.
1079
- assert ! ( self . expressions . is_empty ( ) ) ;
1133
+ assert_eq ! ( self . pushed , 0 ) ;
1080
1134
fcx. tcx . types . never
1081
1135
}
1082
1136
}
1083
1137
}
1138
+
1139
+ /// Something that can be converted into an expression to which we can
1140
+ /// apply a coercion.
1141
+ pub trait AsCoercionSite {
1142
+ fn as_coercion_site ( & self ) -> & hir:: Expr ;
1143
+ }
1144
+
1145
+ impl AsCoercionSite for hir:: Expr {
1146
+ fn as_coercion_site ( & self ) -> & hir:: Expr {
1147
+ self
1148
+ }
1149
+ }
1150
+
1151
+ impl AsCoercionSite for P < hir:: Expr > {
1152
+ fn as_coercion_site ( & self ) -> & hir:: Expr {
1153
+ self
1154
+ }
1155
+ }
1156
+
1157
+ impl < ' a , T > AsCoercionSite for & ' a T
1158
+ where T : AsCoercionSite
1159
+ {
1160
+ fn as_coercion_site ( & self ) -> & hir:: Expr {
1161
+ ( * * self ) . as_coercion_site ( )
1162
+ }
1163
+ }
1164
+
1165
+ impl AsCoercionSite for ! {
1166
+ fn as_coercion_site ( & self ) -> & hir:: Expr {
1167
+ unreachable ! ( )
1168
+ }
1169
+ }
1170
+
1171
+ impl AsCoercionSite for hir:: Arm {
1172
+ fn as_coercion_site ( & self ) -> & hir:: Expr {
1173
+ & self . body
1174
+ }
1175
+ }
0 commit comments