[flang] Fix copy elision assumption.
authorMichael Kruse <llvm-project@meinersbur.de>
Mon, 14 Dec 2020 17:05:51 +0000 (11:05 -0600)
committerMichael Kruse <llvm-project@meinersbur.de>
Mon, 14 Dec 2020 20:07:05 +0000 (14:07 -0600)
Before this patch, the Restorer depended on copy elision to happen.
Without copy elision, the function ScopedSet calls the move constructor
before its dtor. The dtor will prematurely restore the reference to the
original value.

Instead of relying the compiler to not use the Restorer's copy
constructor, delete its copy and assign operators. Hence, callers cannot
move or copy a Restorer object anymore, and have to explicitly provide
the reset state. ScopedSet avoids calling move/copy operations by
relying on unnamed return value optimization, which is mandatory in
C++17.

Reviewed By: klausler

Differential Revision: https://reviews.llvm.org/D88797

flang/include/flang/Common/restorer.h
flang/lib/Semantics/check-declarations.cpp

index 47e5423..4d5f5e4 100644 (file)
 namespace Fortran::common {
 template <typename A> class Restorer {
 public:
-  explicit Restorer(A &p) : p_{p}, original_{std::move(p)} {}
+  explicit Restorer(A &p, A original) : p_{p}, original_{std::move(original)} {}
   ~Restorer() { p_ = std::move(original_); }
 
+  // Inhibit any recreation of this restorer that would result in two restorers
+  // trying to restore the same reference.
+  Restorer(const Restorer &) = delete;
+  Restorer(Restorer &&that) = delete;
+  const Restorer &operator=(const Restorer &) = delete;
+  const Restorer &operator=(Restorer &&that) = delete;
+
 private:
   A &p_;
   A original_;
@@ -32,15 +39,15 @@ private:
 
 template <typename A, typename B>
 common::IfNoLvalue<Restorer<A>, B> ScopedSet(A &to, B &&from) {
-  Restorer<A> result{to};
+  A original{std::move(to)};
   to = std::move(from);
-  return result;
+  return Restorer<A>{to, std::move(original)};
 }
 template <typename A, typename B>
 common::IfNoLvalue<Restorer<A>, B> ScopedSet(A &to, const B &from) {
-  Restorer<A> result{to};
+  A original{std::move(to)};
   to = from;
-  return result;
+  return Restorer<A>{to, std::move(original)};
 }
 } // namespace Fortran::common
 #endif // FORTRAN_COMMON_RESTORER_H_
index 0d2e2e8..dd76f67 100644 (file)
@@ -1498,7 +1498,7 @@ void CheckHelper::CheckProcBinding(
 
 void CheckHelper::Check(const Scope &scope) {
   scope_ = &scope;
-  common::Restorer<const Symbol *> restorer{innermostSymbol_};
+  common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_};
   if (const Symbol * symbol{scope.symbol()}) {
     innermostSymbol_ = symbol;
   }