@@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
99use rustc_errors:: struct_span_err;
1010use rustc_hir as hir;
1111use rustc_hir:: def:: Res ;
12+ use rustc_session:: parse:: feature_err;
1213use rustc_span:: hygiene:: ForLoopLoc ;
1314use rustc_span:: source_map:: { respan, DesugaringKind , Span , Spanned } ;
1415use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -146,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
146147 hir:: ExprKind :: Block ( self . lower_block ( blk, opt_label. is_some ( ) ) , opt_label)
147148 }
148149 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 )
150151 }
151152 ExprKind :: AssignOp ( op, ref el, ref er) => hir:: ExprKind :: AssignOp (
152153 self . lower_binop ( op) ,
@@ -840,6 +841,134 @@ impl<'hir> LoweringContext<'_, 'hir> {
840841 } )
841842 }
842843
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+
843972 /// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
844973 fn lower_expr_range_closed ( & mut self , span : Span , e1 : & Expr , e2 : & Expr ) -> hir:: ExprKind < ' hir > {
845974 let e1 = self . lower_expr_mut ( e1) ;
0 commit comments