@@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
9
9
use rustc_errors:: struct_span_err;
10
10
use rustc_hir as hir;
11
11
use rustc_hir:: def:: Res ;
12
+ use rustc_session:: parse:: feature_err;
12
13
use rustc_span:: hygiene:: ForLoopLoc ;
13
14
use rustc_span:: source_map:: { respan, DesugaringKind , Span , Spanned } ;
14
15
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -146,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
146
147
hir:: ExprKind :: Block ( self . lower_block ( blk, opt_label. is_some ( ) ) , opt_label)
147
148
}
148
149
ExprKind :: Assign ( ref el, ref er, span) => {
149
- hir :: ExprKind :: Assign ( self . lower_expr ( el) , self . lower_expr ( er ) , span)
150
+ self . lower_expr_assign ( el, er , span , e . span )
150
151
}
151
152
ExprKind :: AssignOp ( op, ref el, ref er) => hir:: ExprKind :: AssignOp (
152
153
self . lower_binop ( op) ,
@@ -840,6 +841,134 @@ impl<'hir> LoweringContext<'_, 'hir> {
840
841
} )
841
842
}
842
843
844
+ /// Destructure the LHS of complex assignments.
845
+ /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
846
+ fn lower_expr_assign (
847
+ & mut self ,
848
+ lhs : & Expr ,
849
+ rhs : & Expr ,
850
+ eq_sign_span : Span ,
851
+ whole_span : Span ,
852
+ ) -> hir:: ExprKind < ' hir > {
853
+ // Return early in case of an ordinary assignment.
854
+ fn is_ordinary ( lhs : & Expr ) -> bool {
855
+ match & lhs. kind {
856
+ ExprKind :: Tup ( ..) => false ,
857
+ ExprKind :: Paren ( e) => {
858
+ match e. kind {
859
+ // We special-case `(..)` for consistency with patterns.
860
+ ExprKind :: Range ( None , None , RangeLimits :: HalfOpen ) => false ,
861
+ _ => is_ordinary ( e) ,
862
+ }
863
+ }
864
+ _ => true ,
865
+ }
866
+ }
867
+ if is_ordinary ( lhs) {
868
+ return hir:: ExprKind :: Assign ( self . lower_expr ( lhs) , self . lower_expr ( rhs) , eq_sign_span) ;
869
+ }
870
+ if !self . sess . features_untracked ( ) . destructuring_assignment {
871
+ feature_err (
872
+ & self . sess . parse_sess ,
873
+ sym:: destructuring_assignment,
874
+ eq_sign_span,
875
+ "destructuring assignments are unstable" ,
876
+ )
877
+ . span_label ( lhs. span , "cannot assign to this expression" )
878
+ . emit ( ) ;
879
+ }
880
+
881
+ let mut assignments = vec ! [ ] ;
882
+
883
+ // The LHS becomes a pattern: `(lhs1, lhs2)`.
884
+ let pat = self . destructure_assign ( lhs, eq_sign_span, & mut assignments) ;
885
+ let rhs = self . lower_expr ( rhs) ;
886
+
887
+ // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
888
+ let destructure_let = self . stmt_let_pat (
889
+ ThinVec :: new ( ) ,
890
+ whole_span,
891
+ Some ( rhs) ,
892
+ pat,
893
+ hir:: LocalSource :: AssignDesugar ( eq_sign_span) ,
894
+ ) ;
895
+
896
+ // `a = lhs1; b = lhs2;`.
897
+ let stmts = self
898
+ . arena
899
+ . alloc_from_iter ( std:: iter:: once ( destructure_let) . chain ( assignments. into_iter ( ) ) ) ;
900
+
901
+ // Wrap everything in a block.
902
+ hir:: ExprKind :: Block ( & self . block_all ( whole_span, stmts, None ) , None )
903
+ }
904
+
905
+ /// Convert the LHS of a destructuring assignment to a pattern.
906
+ /// Each sub-assignment is recorded in `assignments`.
907
+ fn destructure_assign (
908
+ & mut self ,
909
+ lhs : & Expr ,
910
+ eq_sign_span : Span ,
911
+ assignments : & mut Vec < hir:: Stmt < ' hir > > ,
912
+ ) -> & ' hir hir:: Pat < ' hir > {
913
+ match & lhs. kind {
914
+ // Tuples.
915
+ ExprKind :: Tup ( elements) => {
916
+ let ( pats, rest) =
917
+ self . destructure_sequence ( elements, "tuple" , eq_sign_span, assignments) ;
918
+ let tuple_pat = hir:: PatKind :: Tuple ( pats, rest. map ( |r| r. 0 ) ) ;
919
+ return self . pat_without_dbm ( lhs. span , tuple_pat) ;
920
+ }
921
+ ExprKind :: Paren ( e) => {
922
+ // We special-case `(..)` for consistency with patterns.
923
+ if let ExprKind :: Range ( None , None , RangeLimits :: HalfOpen ) = e. kind {
924
+ let tuple_pat = hir:: PatKind :: Tuple ( & [ ] , Some ( 0 ) ) ;
925
+ return self . pat_without_dbm ( lhs. span , tuple_pat) ;
926
+ } else {
927
+ return self . destructure_assign ( e, eq_sign_span, assignments) ;
928
+ }
929
+ }
930
+ _ => { }
931
+ }
932
+ // Treat all other cases as normal lvalue.
933
+ let ident = Ident :: new ( sym:: lhs, lhs. span ) ;
934
+ let ( pat, binding) = self . pat_ident ( lhs. span , ident) ;
935
+ let ident = self . expr_ident ( lhs. span , ident, binding) ;
936
+ let assign = hir:: ExprKind :: Assign ( self . lower_expr ( lhs) , ident, eq_sign_span) ;
937
+ let expr = self . expr ( lhs. span , assign, ThinVec :: new ( ) ) ;
938
+ assignments. push ( self . stmt_expr ( lhs. span , expr) ) ;
939
+ pat
940
+ }
941
+
942
+ /// Destructure a sequence of expressions occurring on the LHS of an assignment.
943
+ /// Such a sequence occurs in a tuple (struct)/slice.
944
+ /// Return a sequence of corresponding patterns, and the index and the span of `..` if it
945
+ /// exists.
946
+ /// Each sub-assignment is recorded in `assignments`.
947
+ fn destructure_sequence (
948
+ & mut self ,
949
+ elements : & [ AstP < Expr > ] ,
950
+ ctx : & str ,
951
+ eq_sign_span : Span ,
952
+ assignments : & mut Vec < hir:: Stmt < ' hir > > ,
953
+ ) -> ( & ' hir [ & ' hir hir:: Pat < ' hir > ] , Option < ( usize , Span ) > ) {
954
+ let mut rest = None ;
955
+ let elements =
956
+ self . arena . alloc_from_iter ( elements. iter ( ) . enumerate ( ) . filter_map ( |( i, e) | {
957
+ // Check for `..` pattern.
958
+ if let ExprKind :: Range ( None , None , RangeLimits :: HalfOpen ) = e. kind {
959
+ if let Some ( ( _, prev_span) ) = rest {
960
+ self . ban_extra_rest_pat ( e. span , prev_span, ctx) ;
961
+ } else {
962
+ rest = Some ( ( i, e. span ) ) ;
963
+ }
964
+ None
965
+ } else {
966
+ Some ( self . destructure_assign ( e, eq_sign_span, assignments) )
967
+ }
968
+ } ) ) ;
969
+ ( elements, rest)
970
+ }
971
+
843
972
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
844
973
fn lower_expr_range_closed ( & mut self , span : Span , e1 : & Expr , e2 : & Expr ) -> hir:: ExprKind < ' hir > {
845
974
let e1 = self . lower_expr_mut ( e1) ;
0 commit comments