Blame SOURCES/rhbz1337416.patch

9201c6
commit 056cb27baac1ce3ab4d675dbbe4881afde801ca3
9201c6
Author: Frank Ch. Eigler <fche@redhat.com>
9201c6
Date:   Wed Jun 22 11:43:33 2016 -0400
9201c6
9201c6
    PR18079: support nested autocast / @defined
9201c6
    
9201c6
    We now perform const-folding & dead-code-elision during the type
9201c6
    resolution loop, whenever an autocast expression gets evaluated.  This
9201c6
    way, @defined(foo()->mm) type expressions can work as nature intended.
9201c6
    
9201c6
    This requires @defined() not to be short-circuit evaluated to 0 during
9201c6
    a random const_folding process, so a flag is introduced to control its
9201c6
    preservation or collapsing.  For the last (assert_resolvability) pass
9201c6
    in the type resolution loop, this flag is set to true, so that
9201c6
    genuinely unresolvable @defined($expressions) do get mapped to 0 in
9201c6
    time for a last elision.
9201c6
9201c6
diff --git a/elaborate.cxx b/elaborate.cxx
9201c6
index 4a375d9..a1088a1 100644
9201c6
--- a/elaborate.cxx
9201c6
+++ b/elaborate.cxx
9201c6
@@ -3984,9 +3984,10 @@ struct const_folder: public update_visitor
9201c6
 {
9201c6
   systemtap_session& session;
9201c6
   bool& relaxed_p;
9201c6
-
9201c6
-  const_folder(systemtap_session& s, bool& r):
9201c6
-    session(s), relaxed_p(r), last_number(0), last_string(0) {}
9201c6
+  bool collapse_defines_p;
9201c6
+  
9201c6
+  const_folder(systemtap_session& s, bool& r, bool collapse_defines = false):
9201c6
+    session(s), relaxed_p(r), collapse_defines_p(collapse_defines), last_number(0), last_string(0) {}
9201c6
 
9201c6
   literal_number* last_number;
9201c6
   literal_number* get_number(expression*& e);
9201c6
@@ -4506,15 +4507,26 @@ const_folder::visit_ternary_expression (ternary_expression* e)
9201c6
 void
9201c6
 const_folder::visit_defined_op (defined_op* e)
9201c6
 {
9201c6
-  // If a @defined makes it this far, then it is, de facto, undefined.
9201c6
-
9201c6
-  if (session.verbose>2)
9201c6
-    clog << _("Collapsing untouched @defined check ") << *e->tok << endl;
9201c6
-  relaxed_p = false;
9201c6
+  // If a @defined makes it this far, then it was not resolved by
9201c6
+  // previous efforts.  We could assume that therefore it is a big fat
9201c6
+  // zero, but for the @defined(autocast) case PR18079, this just
9201c6
+  // means that we didn't know yet.
9201c6
 
9201c6
-  literal_number* n = new literal_number (0);
9201c6
-  n->tok = e->tok;
9201c6
-  n->visit (this);
9201c6
+  if (collapse_defines_p)
9201c6
+    {
9201c6
+      if (session.verbose>2)
9201c6
+        clog << _("Collapsing untouched @defined check ") << *e->tok << endl;
9201c6
+       relaxed_p = false;
9201c6
+       literal_number* n = new literal_number (0);
9201c6
+       n->tok = e->tok;
9201c6
+       n->visit (this);
9201c6
+    }
9201c6
+  else
9201c6
+    {
9201c6
+      if (session.verbose>2)
9201c6
+        clog << _("Preserving unresolved @defined check ") << *e->tok << endl;
9201c6
+      provide (e);
9201c6
+    }
9201c6
 }
9201c6
 
9201c6
 void
9201c6
@@ -5387,6 +5399,21 @@ semantic_pass_types (systemtap_session& s)
9201c6
             ti.current_probe = 0;
9201c6
             ti.current_function = fd;
9201c6
             ti.t = pe_unknown;
9201c6
+
9201c6
+            if (ti.assert_resolvability)
9201c6
+              {
9201c6
+                // PR18079, rerun the const-folder / dead-block-remover
9201c6
+                // one last time, in case an unresolvable
9201c6
+                // @defined($foobar) still persists.  This should map
9201c6
+                // those to 0.
9201c6
+                bool relaxed_p;
9201c6
+                const_folder cf (s, relaxed_p, true); // NB: true
9201c6
+                cf.replace (fd->body);
9201c6
+                dead_control_remover dc (s, relaxed_p);
9201c6
+                fd->body->visit (&dc);
9201c6
+                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+              }
9201c6
+              
9201c6
             fd->body->visit (& ti);
9201c6
             // NB: we don't have to assert a known type for
9201c6
             // functions here, to permit a "void" function.
9201c6
@@ -5402,6 +5429,16 @@ semantic_pass_types (systemtap_session& s)
9201c6
               {
9201c6
                 autocast_expanding_visitor aev (ti);
9201c6
                 aev.replace (fd->body);
9201c6
+
9201c6
+                // PR18079, rerun the const-folder / dead-block-remover
9201c6
+                // in case autocast evaluation enabled a @defined()
9201c6
+                bool relaxed_p;
9201c6
+                const_folder cf (s, relaxed_p);
9201c6
+                cf.replace (fd->body);
9201c6
+                dead_control_remover dc (s, relaxed_p);
9201c6
+                fd->body->visit (&dc);
9201c6
+                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+
9201c6
                 ti.num_available_autocasts = 0;
9201c6
               }
9201c6
           }
9201c6
@@ -5420,6 +5457,21 @@ semantic_pass_types (systemtap_session& s)
9201c6
             ti.current_function = 0;
9201c6
             ti.current_probe = pn;
9201c6
             ti.t = pe_unknown;
9201c6
+
9201c6
+            if (ti.assert_resolvability)
9201c6
+              {
9201c6
+                // PR18079, rerun the const-folder / dead-block-remover
9201c6
+                // one last time, in case an unresolvable
9201c6
+                // @defined($foobar) still persists.  This should map
9201c6
+                // those to 0.
9201c6
+                bool relaxed_p;
9201c6
+                const_folder cf (s, relaxed_p, true); // NB: true
9201c6
+                cf.replace (pn->body);
9201c6
+                dead_control_remover dc (s, relaxed_p);
9201c6
+                pn->body->visit (&dc);
9201c6
+                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+              }
9201c6
+            
9201c6
             pn->body->visit (& ti);
9201c6
             for (unsigned i=0; i < pn->locals.size(); ++i)
9201c6
               ti.check_local (pn->locals[i]);
9201c6
@@ -5429,6 +5481,16 @@ semantic_pass_types (systemtap_session& s)
9201c6
               {
9201c6
                 autocast_expanding_visitor aev (ti);
9201c6
                 aev.replace (pn->body);
9201c6
+
9201c6
+                // PR18079, rerun the const-folder / dead-block-remover
9201c6
+                // in case autocast evaluation enabled a @defined()
9201c6
+                bool relaxed_p;
9201c6
+                const_folder cf (s, relaxed_p);
9201c6
+                cf.replace (pn->body);
9201c6
+                dead_control_remover dc (s, relaxed_p);
9201c6
+                pn->body->visit (&dc);
9201c6
+                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+
9201c6
                 ti.num_available_autocasts = 0;
9201c6
               }
9201c6
             
9201c6
@@ -5907,7 +5969,15 @@ typeresolution_info::visit_target_symbol (target_symbol* e)
9201c6
   // later unused-expression-elimination pass didn't get rid of it
9201c6
   // either.  So we have a target symbol that is believed to be of
9201c6
   // genuine use, yet unresolved by the provider.
9201c6
-
9201c6
+  //
9201c6
+  // PR18079, or it can happen if a $target expression is nested within
9201c6
+  // a @defined() test that has not yet been resolved (but can be soon).
9201c6
+  if (! assert_resolvability)
9201c6
+    {
9201c6
+      num_still_unresolved ++;
9201c6
+      return;
9201c6
+    }
9201c6
+  
9201c6
   if (session.verbose > 2)
9201c6
     {
9201c6
       clog << _("Resolution problem with ");
9201c6
@@ -5974,7 +6044,15 @@ typeresolution_info::visit_atvar_op (atvar_op* e)
9201c6
 void
9201c6
 typeresolution_info::visit_defined_op (defined_op* e)
9201c6
 {
9201c6
-  throw SEMANTIC_ERROR(_("unexpected @defined"), e->tok);
9201c6
+  // PR18079: if a @defined is still around, it may have a parameter that
9201c6
+  // wasn't resolvable one way or another earlier.  Maybe an autocast_op.
9201c6
+  // Let's give it a visit just in case. 
9201c6
+  e->operand->visit(this);
9201c6
+
9201c6
+  if (assert_resolvability)
9201c6
+    throw SEMANTIC_ERROR(_("unexpected @defined"), e->tok);
9201c6
+  else
9201c6
+    num_still_unresolved ++;
9201c6
 }
9201c6
 
9201c6
 
9201c6
diff --git a/testsuite/semok/autocast14.stp b/testsuite/semok/autocast14.stp
9201c6
index 55c06c4..1b32d80 100755
9201c6
--- a/testsuite/semok/autocast14.stp
9201c6
+++ b/testsuite/semok/autocast14.stp
9201c6
@@ -1,7 +1,7 @@
9201c6
 #! stap -p2
9201c6
 
9201c6
-probe oneshot
9201c6
-{
9201c6
+
9201c6
+@define STUFF %(
9201c6
     // precheck, it should work with @cast
9201c6
     if (!@defined(@task(0)->mm)) {
9201c6
         println($cast_failed_mm)
9201c6
@@ -17,4 +17,16 @@ probe oneshot
9201c6
     if (@defined(task_current()->systemtap)) {
9201c6
         println($autocast_succeeded_systemtap)
9201c6
     }
9201c6
+%)
9201c6
+
9201c6
+
9201c6
+probe oneshot
9201c6
+{
9201c6
+  @STUFF
9201c6
+  foo() // from a function too, to test PR18079 function processing
9201c6
 }
9201c6
+
9201c6
+function foo ()
9201c6
+{
9201c6
+  @STUFF
9201c6
+}
9201c6
\ No newline at end of file
9201c6
9201c6
commit 0eda9cd7c9fe3cf7622f6bcf5e9cfba9fdf537dd
9201c6
Author: Josh Stone <jistone@redhat.com>
9201c6
Date:   Wed Jun 22 12:09:05 2016 -0700
9201c6
9201c6
    Increase the difficulty of semok/autocast14.stp
9201c6
9201c6
diff --git a/testsuite/semok/autocast14.stp b/testsuite/semok/autocast14.stp
9201c6
index 1b32d80..b9488d7 100755
9201c6
--- a/testsuite/semok/autocast14.stp
9201c6
+++ b/testsuite/semok/autocast14.stp
9201c6
@@ -17,6 +17,19 @@
9201c6
     if (@defined(task_current()->systemtap)) {
9201c6
         println($autocast_succeeded_systemtap)
9201c6
     }
9201c6
+
9201c6
+    // Test that autocast can resolve on the results of @defined
9201c6
+    mm1 = @choose_defined($nonsense, task_current())->mm;
9201c6
+    mm2 = @choose_defined(task_current(), $nonsense)->mm;
9201c6
+    println(mm1 == mm2)
9201c6
+
9201c6
+    // Test an even deeper level of @defined
9201c6
+    if (!@defined(mm1->mmap) || !@defined(mm2->mmap)) {
9201c6
+        println($autocast_failed_mm_mmap)
9201c6
+    }
9201c6
+    if (@defined(mm1->systemtap) || @defined(mm2->systemtap)) {
9201c6
+        println($autocast_succeeded_mm_systemtap)
9201c6
+    }
9201c6
 %)
9201c6
 
9201c6
 
9201c6
@@ -29,4 +42,4 @@ probe oneshot
9201c6
 function foo ()
9201c6
 {
9201c6
   @STUFF
9201c6
-}
9201c6
\ No newline at end of file
9201c6
+}
9201c6
9201c6
commit 048b546d5645abb6e6ef5148c4ddbd170600e1d3
9201c6
Author: Josh Stone <jistone@redhat.com>
9201c6
Date:   Fri Jul 8 18:21:49 2016 -0700
9201c6
9201c6
    Tweak autocast-defined interactions further
9201c6
    
9201c6
    - collapse basic @defined($foo) right away.
9201c6
    - last-ditch collapse other @defined(expr) to 1 or 0 depending on pe_unknown.
9201c6
    - run that last-ditch effort *before* turning on assert_resolvability.
9201c6
    - only run extra dead_control_remover for optimized runs
9201c6
    - in var_expanding_visitor, pass *any* unchanged expr through, so they
9201c6
      may be decided later.  (e.g. for @choose_defined ternaries)
9201c6
9201c6
diff --git a/elaborate.cxx b/elaborate.cxx
9201c6
index a1088a1..fd6ccce 100644
9201c6
--- a/elaborate.cxx
9201c6
+++ b/elaborate.cxx
9201c6
@@ -3987,7 +3987,8 @@ struct const_folder: public update_visitor
9201c6
   bool collapse_defines_p;
9201c6
   
9201c6
   const_folder(systemtap_session& s, bool& r, bool collapse_defines = false):
9201c6
-    session(s), relaxed_p(r), collapse_defines_p(collapse_defines), last_number(0), last_string(0) {}
9201c6
+    session(s), relaxed_p(r), collapse_defines_p(collapse_defines),
9201c6
+    last_number(0), last_string(0), last_target_symbol(0) {}
9201c6
 
9201c6
   literal_number* last_number;
9201c6
   literal_number* get_number(expression*& e);
9201c6
@@ -4011,6 +4012,9 @@ struct const_folder: public update_visitor
9201c6
   void visit_concatenation (concatenation* e);
9201c6
   void visit_ternary_expression (ternary_expression* e);
9201c6
   void visit_defined_op (defined_op* e);
9201c6
+
9201c6
+  target_symbol* last_target_symbol;
9201c6
+  target_symbol* get_target_symbol(expression*& e);
9201c6
   void visit_target_symbol (target_symbol* e);
9201c6
 };
9201c6
 
9201c6
@@ -4511,15 +4515,35 @@ const_folder::visit_defined_op (defined_op* e)
9201c6
   // previous efforts.  We could assume that therefore it is a big fat
9201c6
   // zero, but for the @defined(autocast) case PR18079, this just
9201c6
   // means that we didn't know yet.
9201c6
+  int64_t value = 0;
9201c6
+  bool collapse_this = false;
9201c6
 
9201c6
-  if (collapse_defines_p)
9201c6
+  // We do know that plain target_symbols aren't going anywhere though.
9201c6
+  if (get_target_symbol (e->operand))
9201c6
+    {
9201c6
+      if (session.verbose>2)
9201c6
+        clog << _("Collapsing target_symbol @defined check ") << *e->tok << endl;
9201c6
+      collapse_this = true;
9201c6
+    }
9201c6
+  else if (collapse_defines_p && relaxed_p)
9201c6
     {
9201c6
       if (session.verbose>2)
9201c6
         clog << _("Collapsing untouched @defined check ") << *e->tok << endl;
9201c6
-       relaxed_p = false;
9201c6
-       literal_number* n = new literal_number (0);
9201c6
-       n->tok = e->tok;
9201c6
-       n->visit (this);
9201c6
+
9201c6
+      // If we got to an expression with a known type, call it defined.
9201c6
+      if (e->operand->type != pe_unknown)
9201c6
+        value = 1;
9201c6
+      collapse_this = true;
9201c6
+    }
9201c6
+
9201c6
+  if (collapse_this)
9201c6
+    {
9201c6
+      // Don't be greedy... we'll only collapse one at a time so type
9201c6
+      // resolution can have another go at it.
9201c6
+      relaxed_p = false;
9201c6
+      literal_number* n = new literal_number (value);
9201c6
+      n->tok = e->tok;
9201c6
+      n->visit (this);
9201c6
     }
9201c6
   else
9201c6
     {
9201c6
@@ -4529,6 +4553,13 @@ const_folder::visit_defined_op (defined_op* e)
9201c6
     }
9201c6
 }
9201c6
 
9201c6
+target_symbol*
9201c6
+const_folder::get_target_symbol(expression*& e)
9201c6
+{
9201c6
+  replace (e);
9201c6
+  return (e == last_target_symbol) ? last_target_symbol : NULL;
9201c6
+}
9201c6
+
9201c6
 void
9201c6
 const_folder::visit_target_symbol (target_symbol* e)
9201c6
 {
9201c6
@@ -4545,7 +4576,10 @@ const_folder::visit_target_symbol (target_symbol* e)
9201c6
       relaxed_p = false;
9201c6
     }
9201c6
   else
9201c6
-    update_visitor::visit_target_symbol (e);
9201c6
+    {
9201c6
+      update_visitor::visit_target_symbol (e);
9201c6
+      last_target_symbol = e;
9201c6
+    }
9201c6
 }
9201c6
 
9201c6
 static int initial_typeres_pass(systemtap_session& s);
9201c6
@@ -5400,20 +5434,6 @@ semantic_pass_types (systemtap_session& s)
9201c6
             ti.current_function = fd;
9201c6
             ti.t = pe_unknown;
9201c6
 
9201c6
-            if (ti.assert_resolvability)
9201c6
-              {
9201c6
-                // PR18079, rerun the const-folder / dead-block-remover
9201c6
-                // one last time, in case an unresolvable
9201c6
-                // @defined($foobar) still persists.  This should map
9201c6
-                // those to 0.
9201c6
-                bool relaxed_p;
9201c6
-                const_folder cf (s, relaxed_p, true); // NB: true
9201c6
-                cf.replace (fd->body);
9201c6
-                dead_control_remover dc (s, relaxed_p);
9201c6
-                fd->body->visit (&dc);
9201c6
-                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
-              }
9201c6
-              
9201c6
             fd->body->visit (& ti);
9201c6
             // NB: we don't have to assert a known type for
9201c6
             // functions here, to permit a "void" function.
9201c6
@@ -5431,13 +5451,19 @@ semantic_pass_types (systemtap_session& s)
9201c6
                 aev.replace (fd->body);
9201c6
 
9201c6
                 // PR18079, rerun the const-folder / dead-block-remover
9201c6
-                // in case autocast evaluation enabled a @defined()
9201c6
-                bool relaxed_p;
9201c6
-                const_folder cf (s, relaxed_p);
9201c6
-                cf.replace (fd->body);
9201c6
-                dead_control_remover dc (s, relaxed_p);
9201c6
-                fd->body->visit (&dc);
9201c6
-                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+                // if autocast evaluation enabled a @defined()
9201c6
+                if (aev.count_replaced_defined_ops() > 0)
9201c6
+                  {
9201c6
+                    bool relaxed_p = true;
9201c6
+                    const_folder cf (s, relaxed_p);
9201c6
+                    cf.replace (fd->body);
9201c6
+                    if (! s.unoptimized)
9201c6
+                      {
9201c6
+                        dead_control_remover dc (s, relaxed_p);
9201c6
+                        fd->body->visit (&dc);
9201c6
+                      }
9201c6
+                    (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+                  }
9201c6
 
9201c6
                 ti.num_available_autocasts = 0;
9201c6
               }
9201c6
@@ -5458,20 +5484,6 @@ semantic_pass_types (systemtap_session& s)
9201c6
             ti.current_probe = pn;
9201c6
             ti.t = pe_unknown;
9201c6
 
9201c6
-            if (ti.assert_resolvability)
9201c6
-              {
9201c6
-                // PR18079, rerun the const-folder / dead-block-remover
9201c6
-                // one last time, in case an unresolvable
9201c6
-                // @defined($foobar) still persists.  This should map
9201c6
-                // those to 0.
9201c6
-                bool relaxed_p;
9201c6
-                const_folder cf (s, relaxed_p, true); // NB: true
9201c6
-                cf.replace (pn->body);
9201c6
-                dead_control_remover dc (s, relaxed_p);
9201c6
-                pn->body->visit (&dc);
9201c6
-                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
-              }
9201c6
-            
9201c6
             pn->body->visit (& ti);
9201c6
             for (unsigned i=0; i < pn->locals.size(); ++i)
9201c6
               ti.check_local (pn->locals[i]);
9201c6
@@ -5483,13 +5495,19 @@ semantic_pass_types (systemtap_session& s)
9201c6
                 aev.replace (pn->body);
9201c6
 
9201c6
                 // PR18079, rerun the const-folder / dead-block-remover
9201c6
-                // in case autocast evaluation enabled a @defined()
9201c6
-                bool relaxed_p;
9201c6
-                const_folder cf (s, relaxed_p);
9201c6
-                cf.replace (pn->body);
9201c6
-                dead_control_remover dc (s, relaxed_p);
9201c6
-                pn->body->visit (&dc);
9201c6
-                (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+                // if autocast evaluation enabled a @defined()
9201c6
+                if (aev.count_replaced_defined_ops() > 0)
9201c6
+                  {
9201c6
+                    bool relaxed_p = true;
9201c6
+                    const_folder cf (s, relaxed_p);
9201c6
+                    cf.replace (pn->body);
9201c6
+                    if (! s.unoptimized)
9201c6
+                      {
9201c6
+                        dead_control_remover dc (s, relaxed_p);
9201c6
+                        pn->body->visit (&dc);
9201c6
+                      }
9201c6
+                    (void) relaxed_p; // we judge success later by num_still_unresolved, not this flag
9201c6
+                  }
9201c6
 
9201c6
                 ti.num_available_autocasts = 0;
9201c6
               }
9201c6
@@ -5526,9 +5544,27 @@ semantic_pass_types (systemtap_session& s)
9201c6
             break; // successfully
9201c6
           else if (! ti.assert_resolvability)
9201c6
             {
9201c6
-              ti.assert_resolvability = true; // last pass, with error msgs
9201c6
-              if (s.verbose > 0)
9201c6
-                ti.mismatch_complexity = 0; // print every kind of mismatch
9201c6
+              // PR18079, before we go asserting anything, try to nullify any
9201c6
+              // still-unresolved @defined ops.
9201c6
+              bool relaxed_p = true;
9201c6
+              const_folder cf (s, relaxed_p, true); // NB: true
9201c6
+
9201c6
+              for (auto it = s.probes.begin(); it != s.probes.end(); ++it)
9201c6
+                cf.replace ((*it)->body);
9201c6
+              for (auto it = s.functions.begin(); it != s.functions.end(); ++it)
9201c6
+                cf.replace (it->second->body);
9201c6
+
9201c6
+              if (! s.unoptimized)
9201c6
+                semantic_pass_dead_control (s, relaxed_p);
9201c6
+
9201c6
+              if (! relaxed_p)
9201c6
+                ti.mismatch_complexity = 0; // reset for next pass
9201c6
+              else
9201c6
+                {
9201c6
+                  ti.assert_resolvability = true; // last pass, with error msgs
9201c6
+                  if (s.verbose > 0)
9201c6
+                    ti.mismatch_complexity = 0; // print every kind of mismatch
9201c6
+                }
9201c6
             }
9201c6
           else
9201c6
             { // unsuccessful conclusion
9201c6
diff --git a/tapsets.cxx b/tapsets.cxx
9201c6
index 069966b..6d82069 100644
9201c6
--- a/tapsets.cxx
9201c6
+++ b/tapsets.cxx
9201c6
@@ -2835,7 +2835,8 @@ private:
9201c6
 unsigned var_expanding_visitor::tick = 0;
9201c6
 
9201c6
 
9201c6
-var_expanding_visitor::var_expanding_visitor (): op()
9201c6
+var_expanding_visitor::var_expanding_visitor ():
9201c6
+  replaced_defined_ops(0), op()
9201c6
 {
9201c6
   // FIXME: for the time being, by default we only support plain '$foo
9201c6
   // = bar', not '+=' or any other op= variant. This is fixable, but a
9201c6
@@ -2964,6 +2965,7 @@ var_expanding_visitor::visit_delete_statement (delete_statement* s)
9201c6
 void
9201c6
 var_expanding_visitor::visit_defined_op (defined_op* e)
9201c6
 {
9201c6
+  expression * const old_operand = e->operand;
9201c6
   bool resolved = true;
9201c6
 
9201c6
   defined_ops.push (e);
9201c6
@@ -2999,11 +3001,12 @@ var_expanding_visitor::visit_defined_op (defined_op* e)
9201c6
     target_symbol* tsym = dynamic_cast<target_symbol*> (e->operand);
9201c6
     if (tsym && tsym->saved_conversion_error) // failing
9201c6
       resolved = false;
9201c6
-    else if (tsym) // unresolved but not marked failing
9201c6
+    else if (e->operand == old_operand) // unresolved but not marked failing
9201c6
       {
9201c6
         // There are some visitors that won't touch certain target_symbols,
9201c6
         // e.g. dwarf_var_expanding_visitor won't resolve @cast.  We should
9201c6
         // leave it for now so some other visitor can have a chance.
9201c6
+        defined_ops.pop ();
9201c6
         provide (e);
9201c6
         return;
9201c6
       }
9201c6
@@ -3017,6 +3020,7 @@ var_expanding_visitor::visit_defined_op (defined_op* e)
9201c6
   literal_number* ln = new literal_number (resolved ? 1 : 0);
9201c6
   ln->tok = e->tok;
9201c6
   provide (ln);
9201c6
+  ++replaced_defined_ops;
9201c6
 }
9201c6
 
9201c6
 
9201c6
diff --git a/tapsets.h b/tapsets.h
9201c6
index d630dbb..cb73a7e 100644
9201c6
--- a/tapsets.h
9201c6
+++ b/tapsets.h
9201c6
@@ -61,11 +61,6 @@ public:
9201c6
 
9201c6
 struct var_expanding_visitor: public update_visitor
9201c6
 {
9201c6
-  static unsigned tick;
9201c6
-  std::stack<defined_op*> defined_ops;
9201c6
-  std::set<std::string> valid_ops;
9201c6
-  interned_string* op;
9201c6
-
9201c6
   var_expanding_visitor ();
9201c6
   void visit_assignment (assignment* e);
9201c6
   void visit_pre_crement (pre_crement* e);
9201c6
@@ -73,6 +68,15 @@ struct var_expanding_visitor: public update_visitor
9201c6
   void visit_delete_statement (delete_statement* s);
9201c6
   void visit_defined_op (defined_op* e);
9201c6
 
9201c6
+  unsigned count_replaced_defined_ops () { return replaced_defined_ops; }
9201c6
+
9201c6
+protected:
9201c6
+  static unsigned tick;
9201c6
+  unsigned replaced_defined_ops;
9201c6
+  std::stack<defined_op*> defined_ops;
9201c6
+  std::set<std::string> valid_ops;
9201c6
+  interned_string* op;
9201c6
+
9201c6
   void provide_lvalue_call(functioncall* fcall);
9201c6
 
9201c6
 private:
9201c6
diff --git a/testsuite/semok/autocast14.stp b/testsuite/semok/autocast14.stp
9201c6
index b9488d7..18028e8 100755
9201c6
--- a/testsuite/semok/autocast14.stp
9201c6
+++ b/testsuite/semok/autocast14.stp
9201c6
@@ -30,6 +30,13 @@
9201c6
     if (@defined(mm1->systemtap) || @defined(mm2->systemtap)) {
9201c6
         println($autocast_succeeded_mm_systemtap)
9201c6
     }
9201c6
+
9201c6
+    // Test that autocast can resolve through nested @defined
9201c6
+    // (especially that the ternary isn't automatically "defined")
9201c6
+    mm3 = @choose_defined(@choose_defined($nonsense, $wut), task_current())->mm;
9201c6
+    mm4 = @choose_defined(@choose_defined($nonsense, task_current()), $wut)->mm;
9201c6
+    mm5 = @choose_defined(@choose_defined(task_current(), $nonsense), $wut)->mm;
9201c6
+    println(mm3 == mm4 && mm4 == mm5)
9201c6
 %)
9201c6
 
9201c6