From f569ebe9bcfdeeae94952d9105c33dbf6b545da7 Mon Sep 17 00:00:00 2001
From: Jeff Bezanson <jeff.bezanson@gmail.com>
Date: Sun, 4 Mar 2018 10:47:29 -0500
Subject: [PATCH] lower top-level statements in such a way that the front-end
 knows (#26304)

their values are not used

fixes most false positives in the deprecation for using the value of `.=`
---
 src/ast.c                | 18 +++++++++++++++++-
 src/jlfrontend.scm       | 17 ++++++++++++-----
 src/julia.h              |  1 +
 src/toplevel.c           |  2 +-
 test/deprecation_exec.jl | 12 ++++++++++++
 5 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index d6c8968350187..84da490035742 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -829,7 +829,11 @@ jl_value_t *jl_parse_eval_all(const char *fname,
                     form = jl_expand_macros(form, inmodule, NULL, 0);
                     expression = julia_to_scm(fl_ctx, form);
                 }
-                expression = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "jl-expand-to-thunk")), expression);
+                // expand non-final expressions in statement position (value unused)
+                expression =
+                    fl_applyn(fl_ctx, 1,
+                              symbol_value(symbol(fl_ctx, iscons(cdr_(ast)) ? "jl-expand-to-thunk-stmt" : "jl-expand-to-thunk")),
+                              expression);
             }
             jl_get_ptls_states()->world_age = jl_world_counter;
             form = scm_to_julia(fl_ctx, expression, inmodule);
@@ -1115,6 +1119,18 @@ JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule)
     return expr;
 }
 
+// expand in a context where the expression value is unused
+JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule)
+{
+    JL_TIMING(LOWERING);
+    JL_GC_PUSH1(&expr);
+    expr = jl_copy_ast(expr);
+    expr = jl_expand_macros(expr, inmodule, NULL, 0);
+    expr = jl_call_scm_on_ast("jl-expand-to-thunk-stmt", expr, inmodule);
+    JL_GC_POP();
+    return expr;
+}
+
 
 #ifdef __cplusplus
 }
diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm
index 8dc9a853f5014..9572225af114b 100644
--- a/src/jlfrontend.scm
+++ b/src/jlfrontend.scm
@@ -94,12 +94,14 @@
 
 (define *in-expand* #f)
 
+(define (toplevel-only-expr? e)
+  (and (pair? e)
+       (or (memq (car e) '(toplevel line module import importall using export
+                                    error incomplete))
+           (and (eq? (car e) 'global) (every symbol? (cdr e))))))
+
 (define (expand-toplevel-expr e)
-  (cond ((or (atom? e)
-             (and (pair? e)
-                  (or (memq (car e) '(toplevel line module import importall using export
-                                               error incomplete))
-                      (and (eq? (car e) 'global) (every symbol? (cdr e))))))
+  (cond ((or (atom? e) (toplevel-only-expr? e))
          (if (underscore-symbol? e)
              (syntax-deprecation "underscores as an rvalue" "" #f))
          e)
@@ -207,6 +209,11 @@
   (parser-wrap (lambda ()
                  (expand-toplevel-expr expr))))
 
+(define (jl-expand-to-thunk-stmt expr)
+  (jl-expand-to-thunk (if (toplevel-only-expr? expr)
+                          expr
+                          `(block ,expr (null)))))
+
 ; run whole frontend on a string. useful for testing.
 (define (fe str)
   (expand-toplevel-expr (julia-parse str)))
diff --git a/src/julia.h b/src/julia.h
index de449173e0100..9b11afdfffd80 100644
--- a/src/julia.h
+++ b/src/julia.h
@@ -1450,6 +1450,7 @@ JL_DLLEXPORT jl_value_t *jl_parse_string(const char *str, size_t len,
 JL_DLLEXPORT jl_value_t *jl_load_file_string(const char *text, size_t len,
                                              char *filename, jl_module_t *inmodule);
 JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule);
+JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule);
 JL_DLLEXPORT jl_value_t *jl_eval_string(const char *str);
 
 // external libraries
diff --git a/src/toplevel.c b/src/toplevel.c
index c3279117dacbe..c8f27868f3ed4 100644
--- a/src/toplevel.c
+++ b/src/toplevel.c
@@ -228,7 +228,7 @@ jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex)
         for (int i = 0; i < jl_array_len(exprs); i++) {
             // process toplevel form
             ptls->world_age = jl_world_counter;
-            form = jl_expand(jl_array_ptr_ref(exprs, i), newm);
+            form = jl_expand_stmt(jl_array_ptr_ref(exprs, i), newm);
             ptls->world_age = jl_world_counter;
             (void)jl_toplevel_eval_flex(newm, form, 1, 1);
         }
diff --git a/test/deprecation_exec.jl b/test/deprecation_exec.jl
index 633a65d0a66c5..86ae275dcb0ec 100644
--- a/test/deprecation_exec.jl
+++ b/test/deprecation_exec.jl
@@ -209,6 +209,18 @@ end
     # #6080
     @test_deprecated r"Syntax `&argument`.*is deprecated" Meta.lower(@__MODULE__, :(ccall(:a, Cvoid, (Cint,), &x)))
 
+    @test_logs eval(:(module DotEqualsDep
+        a=[1,2]
+        a.=3
+        0
+        end))
+    @test_logs include_string(@__MODULE__, """
+        a=[1,2]
+        a.=3
+        0""")
+    @test_deprecated include_string(@__MODULE__, """
+        a=[1,2]
+        a.=3""")
 end
 
 module LogTest