protovm: improve stacktrace messages

Change-Id: I62a38178ef3b44f19b1be96ad6366b0865db47c4
diff --git a/src/protovm/error_handling.h b/src/protovm/error_handling.h
index bfbef71..2cac23d 100644
--- a/src/protovm/error_handling.h
+++ b/src/protovm/error_handling.h
@@ -23,6 +23,7 @@
 #include <new>
 #include <string>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "perfetto/base/compiler.h"
@@ -69,26 +70,35 @@
     return status_abort;                                              \
   } while (0)
 
-#define PROTOVM_RETURN(s, ...)                                          \
-  do {                                                                  \
-    if (s.IsAbort()) {                                                  \
-      LogStacktraceMessage(s.stacktrace(),                              \
-                           ::perfetto::base::LoggingBasename(__FILE__), \
-                           __LINE__, ##__VA_ARGS__);                    \
-    }                                                                   \
-    return s;                                                           \
+#define PROTOVM_RETURN(s, ...)                                               \
+  do {                                                                       \
+    static_assert(                                                           \
+        std::is_lvalue_reference_v<decltype((s))> &&                         \
+            !std::is_reference_v<decltype(s)>,                               \
+        "PROTOVM_RETURN accepts only plain local variables. Supporting "     \
+        "references or rvalues would prevent NRVO in the normal (not error " \
+        "handling) path (see \"return std::move\" in "                       \
+        "PROTOVM_RETURN_IF_NOT_OK). Rvalues should be assigned to a local "  \
+        "variable before being passed to PROTOVM_RETURN.");                  \
+    if ((s).IsAbort()) {                                                     \
+      LogStacktraceMessage((s).stacktrace(),                                 \
+                           ::perfetto::base::LoggingBasename(__FILE__),      \
+                           __LINE__, ##__VA_ARGS__);                         \
+    }                                                                        \
+    return (s);                                                              \
   } while (0)
 
 #define PROTOVM_RETURN_IF_NOT_OK(s, ...)                                \
   do {                                                                  \
-    if (s.IsAbort()) {                                                  \
-      LogStacktraceMessage(s.stacktrace(),                              \
+    auto&& status_or = (s);                                             \
+    if (status_or.IsAbort()) {                                          \
+      LogStacktraceMessage(status_or.stacktrace(),                      \
                            ::perfetto::base::LoggingBasename(__FILE__), \
                            __LINE__, ##__VA_ARGS__);                    \
-      return s;                                                         \
+      return std::move(status_or);                                      \
     }                                                                   \
-    if (!s.IsOk()) {                                                    \
-      return s;                                                         \
+    if (!status_or.IsOk()) {                                            \
+      return std::move(status_or);                                      \
     }                                                                   \
   } while (0)
 
diff --git a/src/protovm/parser.cc b/src/protovm/parser.cc
index bc0e1fc..18b2254 100644
--- a/src/protovm/parser.cc
+++ b/src/protovm/parser.cc
@@ -69,12 +69,11 @@
     const protos::pbzero::VmInstruction::Decoder& instruction) {
   if (instruction.has_select()) {
     auto status = ParseSelect(instruction);
-    PROTOVM_RETURN(status, "select");
+    PROTOVM_RETURN(status);
   }
 
   if (instruction.has_reg_load()) {
-    auto status = ParseRegLoad(instruction);
-    PROTOVM_RETURN_IF_NOT_OK(status, "reg_load");
+    PROTOVM_RETURN_IF_NOT_OK(ParseRegLoad(instruction));
   } else if (instruction.has_del()) {
     PROTOVM_ASSIGN_OR_RETURN(bool is_root, cursors_.dst.IsRoot());
     if (cursors_.innermost_saved_dst != nullptr &&
@@ -89,11 +88,9 @@
       PROTOVM_ABORT(
           "del would invalidate a cursor saved by an enclosing select");
     }
-    auto status = executor_->Delete(&cursors_.dst);
-    PROTOVM_RETURN_IF_NOT_OK(status, "del");
+    PROTOVM_RETURN_IF_NOT_OK(executor_->Delete(&cursors_.dst), "del");
   } else if (instruction.has_merge()) {
-    auto status = ParseMerge(instruction);
-    PROTOVM_RETURN_IF_NOT_OK(status, "merge");
+    PROTOVM_RETURN_IF_NOT_OK(ParseMerge(instruction));
   } else if (instruction.has_set()) {
     auto status = executor_->Set(&cursors_);
     PROTOVM_RETURN_IF_NOT_OK(status, "set");
@@ -113,8 +110,7 @@
   if (merge.del_if_src_empty())
     flags |= Executor::MergeFlags::kDelIfSrcEmpty;
   auto status = executor_->Merge(&cursors_, flags);
-  PROTOVM_RETURN_IF_NOT_OK(status);
-  return status;
+  PROTOVM_RETURN(status, "merge");
 }
 
 StatusOr<void> Parser::ParseRegLoad(
@@ -130,9 +126,7 @@
 
   cursors_.selected = saved_selected;
 
-  PROTOVM_RETURN_IF_NOT_OK(status);
-
-  return status;
+  PROTOVM_RETURN(status, "reg_load");
 }
 
 StatusOr<void> Parser::ParseSelect(
@@ -141,9 +135,10 @@
   cursors_.innermost_saved_dst = &saved_cursors.dst;
 
   protos::pbzero::VmOpSelect::Decoder select(instruction.select());
-  cursors_.selected = !select.has_cursor()
-                          ? CursorEnum::VM_CURSOR_SRC
-                          : static_cast<CursorEnum>(select.cursor());
+  CursorEnum selected_cursor = !select.has_cursor()
+                                   ? CursorEnum::VM_CURSOR_SRC
+                                   : static_cast<CursorEnum>(select.cursor());
+  cursors_.selected = selected_cursor;
   cursors_.create_if_not_exist = select.create_if_not_exist();
 
   if (cursors_.selected == CursorEnum::VM_CURSOR_SRC &&
@@ -155,7 +150,7 @@
   auto status = ParseSelectRec(instruction, select.relative_path());
   cursors_ = saved_cursors;
 
-  return status;
+  PROTOVM_RETURN(status, "select (cursor = %d)", selected_cursor);
 }
 
 StatusOr<void> Parser::ParseSelectRec(
@@ -260,7 +255,9 @@
   PROTOVM_RETURN_IF_NOT_OK(status, "enter field (id = %d)",
                            static_cast<int>(curr_component.field_id()));
 
-  return ParseSelectRec(instruction, it_path_component);
+  auto status_select = ParseSelectRec(instruction, it_path_component);
+  PROTOVM_RETURN(status_select, "field (id = %d)",
+                 static_cast<int>(curr_component.field_id()));
 }
 
 }  // namespace protovm