[flang] Fix bug#389, prevent std::move() on forwarded lvalue references
authorpeter klausler <pklausler@nvidia.com>
Mon, 8 Apr 2019 19:29:11 +0000 (12:29 -0700)
committerpeter klausler <pklausler@nvidia.com>
Mon, 8 Apr 2019 21:29:45 +0000 (14:29 -0700)
Original-commit: flang-compiler/f18@9d61c091ad3843056741b0bd4791cec1ac7a583e
Reviewed-on: https://github.com/flang-compiler/f18/pull/390
Tree-same-pre-rewrite: false

22 files changed:
flang/lib/common/idioms.h
flang/lib/common/indirection.h
flang/lib/common/restorer.h
flang/lib/common/template.h
flang/lib/evaluate/expression.h
flang/lib/evaluate/fold.cc
flang/lib/evaluate/tools.h
flang/lib/evaluate/traversal.h
flang/lib/evaluate/variable.h
flang/lib/parser/basic-parsers.h
flang/lib/parser/format-specification.h
flang/lib/parser/grammar.h
flang/lib/parser/message.h
flang/lib/parser/parse-state.h
flang/lib/parser/parse-tree.h
flang/lib/parser/prescan.h
flang/lib/semantics/assignment.cc
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/scope.h
flang/lib/semantics/semantics.h

index 747125e..31f8854 100644 (file)
@@ -136,5 +136,12 @@ template<typename A> struct ListItemCount {
 
 // Given a const reference to a value, return a copy of the value.
 template<typename A> A Clone(const A &x) { return x; }
+
+// Use when declaring functions with rvalue template arguments to dodge
+// confusing C++ reference forwarding semantics, e.g.
+//   template<typename A, NO_LVALUE_REFERENCE(A)> void foo(A &&);
+// Works on parameter packs as well.
+#define NOT_LVALUE_REFERENCE(X) bool = !std::is_lvalue_reference_v<X>
+#define NO_LVALUE_REFERENCE(X) bool = (... && !std::is_lvalue_reference_v<X>)
 }
 #endif  // FORTRAN_COMMON_IDIOMS_H_
index 5ddc2ef..8c9dde5 100644 (file)
@@ -67,8 +67,9 @@ public:
   bool operator!=(const A &that) const { return *p_ != that; }
   bool operator!=(const Indirection &that) const { return *p_ != *that.p_; }
 
-  template<typename... ARGS> static Indirection Make(ARGS &&... args) {
-    return {new A(std::forward<ARGS>(args)...)};
+  template<typename... ARGS, NO_LVALUE_REFERENCE(ARGS)>
+  static Indirection Make(ARGS &&... args) {
+    return {new A(std::move(args)...)};
   }
 
 private:
@@ -120,8 +121,9 @@ public:
   bool operator!=(const A &that) const { return *p_ != that; }
   bool operator!=(const Indirection &that) const { return *p_ != *that.p_; }
 
-  template<typename... ARGS> static Indirection Make(ARGS &&... args) {
-    return {new A(std::forward<ARGS>(args)...)};
+  template<typename... ARGS, NO_LVALUE_REFERENCE(ARGS)>
+  static Indirection Make(ARGS &&... args) {
+    return {new A(std::move(args)...)};
   }
 
 private:
index 13e4618..92908b1 100644 (file)
@@ -24,6 +24,7 @@
 
 #ifndef FORTRAN_COMMON_RESTORER_H_
 #define FORTRAN_COMMON_RESTORER_H_
+#include "idioms.h"
 namespace Fortran::common {
 template<typename A> class Restorer {
 public:
@@ -35,7 +36,8 @@ private:
   A original_;
 };
 
-template<typename A, typename B> Restorer<A> ScopedSet(A &to, B &&from) {
+template<typename A, typename B, NOT_LVALUE_REFERENCE(B)>
+Restorer<A> ScopedSet(A &to, B &&from) {
   Restorer<A> result{to};
   to = std::move(from);
   return result;
index d74e08e..e55e95f 100644 (file)
@@ -113,7 +113,8 @@ std::optional<A> JoinOptional(std::optional<std::optional<A>> &&x) {
 
 // Move a value from one variant type to another.  The types allowed in the
 // source variant must all be allowed in the destination variant type.
-template<typename TOV, typename FROMV> TOV MoveVariant(FROMV &&u) {
+template<typename TOV, typename FROMV, NOT_LVALUE_REFERENCE(FROMV)>
+TOV MoveVariant(FROMV &&u) {
   return std::visit(
       [](auto &&x) -> TOV { return {std::move(x)}; }, std::move(u));
 }
@@ -266,7 +267,7 @@ std::optional<std::tuple<A...>> AllPresent(std::optional<A> &&... x) {
 // N.B. If the function returns std::optional, MapOptional will return
 // std::optional<std::optional<...>> and you will probably want to
 // run it through JoinOptional to "squash" it.
-template<typename R, typename... A>
+template<typename R, typename... A, NO_LVALUE_REFERENCE(A)>
 std::optional<R> MapOptional(
     std::function<R(A &&...)> &&f, std::optional<A> &&... x) {
   if (auto args{AllPresent(std::move(x)...)}) {
@@ -274,7 +275,7 @@ std::optional<R> MapOptional(
   }
   return std::nullopt;
 }
-template<typename R, typename... A>
+template<typename R, typename... A, NO_LVALUE_REFERENCE(A)>
 std::optional<R> MapOptional(R (*f)(A &&...), std::optional<A> &&... x) {
   return MapOptional(std::function<R(A && ...)>{f}, std::move(x)...);
 }
@@ -289,7 +290,7 @@ std::optional<R> MapOptional(R (*f)(A &&...), std::optional<A> &&... x) {
 // and invoke VISITOR::Test<T>() on each until it returns a value that
 // casts to true.  If no invocation of Test succeeds, SearchTypes will
 // return a default-constructed value VISITOR::Result{}.
-template<std::size_t J, typename VISITOR>
+template<std::size_t J, typename VISITOR, NOT_LVALUE_REFERENCE(VISITOR)>
 typename VISITOR::Result SearchTypesHelper(VISITOR &&visitor) {
   using Tuple = typename VISITOR::Types;
   if constexpr (J < std::tuple_size_v<Tuple>) {
@@ -302,7 +303,7 @@ typename VISITOR::Result SearchTypesHelper(VISITOR &&visitor) {
   }
 }
 
-template<typename VISITOR>
+template<typename VISITOR, NOT_LVALUE_REFERENCE(VISITOR)>
 typename VISITOR::Result SearchTypes(VISITOR &&visitor) {
   return SearchTypesHelper<0, VISITOR>(std::move(visitor));
 }
index da92e05..6e3b9d6 100644 (file)
@@ -86,8 +86,7 @@ public:
     return d;
   }
 
-  template<typename A>
-  Derived &operator=(std::enable_if_t<!std::is_reference_v<A>, A> &&x) {
+  template<typename A, NOT_LVALUE_REFERENCE(A)> Derived &operator=(A &&x) {
     Derived &d{derived()};
     d.u = std::move(x);
     return d;
@@ -132,8 +131,7 @@ private:
 public:
   CLASS_BOILERPLATE(Operation)
   explicit Operation(const Expr<OPERANDS> &... x) : operand_{x...} {}
-  explicit Operation(Expr<OPERANDS> &&... x)
-    : operand_{std::forward<Expr<OPERANDS>>(x)...} {}
+  explicit Operation(Expr<OPERANDS> &&... x) : operand_{std::move(x)...} {}
 
   Derived &derived() { return *static_cast<Derived *>(this); }
   const Derived &derived() const { return *static_cast<const Derived *>(this); }
@@ -454,7 +452,9 @@ public:
   ArrayConstructorValues() {}
   bool operator==(const ArrayConstructorValues &) const;
   static constexpr int Rank() { return 1; }
-  template<typename A> void Push(A &&x) { values_.emplace_back(std::move(x)); }
+  template<typename A, NOT_LVALUE_REFERENCE(A)> void Push(A &&x) {
+    values_.emplace_back(std::move(x));
+  }
   Values &values() { return values_; }
   const Values &values() const { return values_; }
 
index 1829124..bc1074f 100644 (file)
@@ -39,7 +39,7 @@
 namespace Fortran::evaluate {
 
 // no-op base case
-template<typename A>
+template<typename A, NOT_LVALUE_REFERENCE(A)>
 Expr<ResultType<A>> FoldOperation(FoldingContext &, A &&x) {
   return Expr<ResultType<A>>{std::move(x)};
 }
@@ -985,7 +985,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
 
 // Unary operations
 
-template<typename TO, typename FROM> std::optional<TO> ConvertString(FROM &&s) {
+template<typename TO, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
+std::optional<TO> ConvertString(FROM &&s) {
   if constexpr (std::is_same_v<TO, FROM>) {
     return std::make_optional<TO>(std::move(s));
   } else {
index 9656482..73feda8 100644 (file)
@@ -96,7 +96,8 @@ template<typename A> bool IsAssumedRank(const Expr<A> &expr) {
 // Generalizing packagers: these take operations and expressions of more
 // specific types and wrap them in Expr<> containers of more abstract types.
 
-template<typename A> Expr<ResultType<A>> AsExpr(A &&x) {
+template<typename A, NOT_LVALUE_REFERENCE(A)>
+Expr<ResultType<A>> AsExpr(A &&x) {
   return Expr<ResultType<A>>{std::move(x)};
 }
 
@@ -110,11 +111,12 @@ Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
   return std::move(x);
 }
 
-template<typename A> Expr<SomeType> AsGenericExpr(A &&x) {
+template<typename A, NOT_LVALUE_REFERENCE(A)>
+Expr<SomeType> AsGenericExpr(A &&x) {
   return Expr<SomeType>{AsCategoryExpr(std::move(x))};
 }
 
-template<typename A>
+template<typename A, NOT_LVALUE_REFERENCE(A)>
 Expr<SomeKind<ResultType<A>::category>> AsCategoryExpr(A &&x) {
   return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
 }
@@ -237,12 +239,12 @@ std::optional<Expr<SomeType>> ConvertToType(
     const semantics::Symbol &, Expr<SomeType> &&);
 
 // Conversions to the type of another expression
-template<TypeCategory TC, int TK, typename FROM>
+template<TypeCategory TC, int TK, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
 Expr<Type<TC, TK>> ConvertTo(const Expr<Type<TC, TK>> &, FROM &&x) {
   return ConvertToType<Type<TC, TK>>(std::move(x));
 }
 
-template<TypeCategory TC, typename FROM>
+template<TypeCategory TC, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
 Expr<SomeKind<TC>> ConvertTo(const Expr<SomeKind<TC>> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toKindExpr) {
@@ -253,7 +255,7 @@ Expr<SomeKind<TC>> ConvertTo(const Expr<SomeKind<TC>> &to, FROM &&from) {
       to.u);
 }
 
-template<typename FROM>
+template<typename FROM, NOT_LVALUE_REFERENCE(FROM)>
 Expr<SomeType> ConvertTo(const Expr<SomeType> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toCatExpr) {
@@ -279,7 +281,7 @@ template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
   VALUE value;
 };
 
-template<TypeCategory TOCAT, typename VALUE>
+template<TypeCategory TOCAT, typename VALUE, NOT_LVALUE_REFERENCE(VALUE)>
 Expr<SomeKind<TOCAT>> ConvertToKind(int kind, VALUE &&x) {
   return common::SearchTypes(
       ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})
index 45c334f..1143fbc 100644 (file)
@@ -142,12 +142,14 @@ private:
 
 class RewriterBase {
 public:
-  template<typename A> A Handle(A &&x) {
+  template<typename A, NOT_LVALUE_REFERENCE(A)> A Handle(A &&x) {
     defaultHandleCalled_ = true;
     return std::move(x);
   }
   template<typename A> void Pre(const A &) {}
-  template<typename A> A Post(A &&x) { return std::move(x); }
+  template<typename A, NOT_LVALUE_REFERENCE(A)> A Post(A &&x) {
+    return std::move(x);
+  }
 
   void Return() { done_ = true; }
 
@@ -168,7 +170,7 @@ private:
   using RewriterBase::done_, RewriterBase::defaultHandleCalled_;
 
 public:
-  template<typename B> B Traverse(B &&x) {
+  template<typename B, NOT_LVALUE_REFERENCE(B)> B Traverse(B &&x) {
     if (!done_) {
       defaultHandleCalled_ = false;
       x = Handle(std::move(x));
index 415924b..02b4d7d 100644 (file)
@@ -183,7 +183,7 @@ public:
   int size() const { return static_cast<int>(subscript_.size()); }
   Subscript &at(int n) { return subscript_.at(n); }
   const Subscript &at(int n) const { return subscript_.at(n); }
-  template<typename A> Subscript &emplace_back(A &&x) {
+  template<typename A, NOT_LVALUE_REFERENCE(A)> Subscript &emplace_back(A &&x) {
     return subscript_.emplace_back(std::move(x));
   }
 
index cabc36b..a322cd2 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+// Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -1192,7 +1192,8 @@ template<typename PA> inline constexpr auto indirect(const PA &p) {
 // that succeeds if a does.  If a succeeds, it then applies many(b >> a).
 // The result is the list of the values returned from all of the applications
 // of a.
-template<typename T> std::list<T> prepend(T &&head, std::list<T> &&rest) {
+template<typename T, NOT_LVALUE_REFERENCE(T)>
+std::list<T> prepend(T &&head, std::list<T> &&rest) {
   rest.push_front(std::move(head));
   return std::move(rest);
 }
index 727bbad..d8e5f15 100644 (file)
@@ -122,10 +122,11 @@ struct FormatItem {
   FormatItem() = delete;
   FormatItem(FormatItem &&) = default;
   FormatItem &operator=(FormatItem &&) = default;
-  template<typename A>
+  template<typename A, NOT_LVALUE_REFERENCE(A)>
   FormatItem(std::optional<std::uint64_t> &&r, A &&x)
     : repeatCount{std::move(r)}, u{std::move(x)} {}
-  template<typename A> explicit FormatItem(A &&x) : u{std::move(x)} {}
+  template<typename A, NOT_LVALUE_REFERENCE(A)>
+  explicit FormatItem(A &&x) : u{std::move(x)} {}
   std::optional<std::uint64_t> repeatCount;
   std::variant<IntrinsicTypeDataEditDesc, DerivedTypeDataEditDesc,
       ControlEditDesc, std::string, std::list<FormatItem>>
index 94917f1..d33d45c 100644 (file)
@@ -2669,7 +2669,8 @@ TYPE_PARSER(first(construct<WaitSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
     construct<WaitSpec>("IOMSG =" >> msgVariable),
     construct<WaitSpec>("IOSTAT =" >> statVariable)))
 
-template<typename A> std::list<A> singletonList(A &&x) {
+template<typename A, NOT_LVALUE_REFERENCE(A)>
+std::list<A> singletonList(A &&x) {
   std::list<A> result;
   result.push_front(std::move(x));
   return result;
index b26b8c3..7d66631 100644 (file)
@@ -141,8 +141,7 @@ public:
 
   template<typename RANGE, typename A1, typename... As>
   Message(RANGE r, const MessageFixedText &t, A1 a1, As... as)
-    : location_{r}, text_{
-                        MessageFormattedText{t, a1, std::forward<As>(as)...}} {}
+    : location_{r}, text_{MessageFormattedText{t, a1, as...}} {}
 
   bool attachmentIsContext() const { return attachmentIsContext_; }
   Reference attachment() const { return attachment_; }
@@ -152,8 +151,9 @@ public:
     attachmentIsContext_ = true;
   }
   Message &Attach(Message *);
-  template<typename... A> Message &Attach(A &&... args) {
-    return Attach(new Message{std::forward<A>(args)...});  // reference-counted
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
+  Message &Attach(A &&... args) {
+    return Attach(new Message{std::move(args)...});  // reference-counted
   }
 
   bool SortBefore(const Message &that) const;
@@ -206,7 +206,7 @@ public:
   bool empty() const { return messages_.empty(); }
 
   template<typename... A> Message &Say(A... args) {
-    last_ = messages_.emplace_after(last_, std::forward<A>(args)...);
+    last_ = messages_.emplace_after(last_, args...);
     return *last_;
   }
 
@@ -254,16 +254,17 @@ public:
     return common::ScopedSet(at_, std::move(at));
   }
 
-  template<typename... A> Message *Say(CharBlock at, A &&... args) {
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
+  Message *Say(CharBlock at, A &&... args) {
     if (messages_ != nullptr) {
-      return &messages_->Say(at, std::forward<A>(args)...);
+      return &messages_->Say(at, std::move(args)...);
     } else {
       return nullptr;
     }
   }
 
-  template<typename... A> Message *Say(A &&... args) {
-    return Say(at_, std::forward<A>(args)...);
+  template<typename... A, NO_LVALUE_REFERENCE(A)> Message *Say(A &&... args) {
+    return Say(at_, std::move(args)...);
   }
 
 private:
index 7c79c7d..15e25f7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+// Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -143,19 +143,18 @@ public:
     context_ = context_->attachment();
   }
 
-  template<typename... A> void Say(CharBlock range, A &&... args) {
+  template<typename... A> void Say(CharBlock range, A... args) {
     if (deferMessages_) {
       anyDeferredMessages_ = true;
     } else {
-      messages_.Say(range, std::forward<A>(args)...).SetContext(context_.get());
+      messages_.Say(range, args...).SetContext(context_.get());
     }
   }
-  template<typename... A> void Say(const MessageFixedText &text, A &&... args) {
-    Say(p_, text, std::forward<A>(args)...);
+  template<typename... A> void Say(const MessageFixedText &text, A... args) {
+    Say(p_, text, args...);
   }
-  template<typename... A>
-  void Say(const MessageExpectedText &text, A &&... args) {
-    Say(p_, text, std::forward<A>(args)...);
+  template<typename... A> void Say(const MessageExpectedText &text, A... args) {
+    Say(p_, text, args...);
   }
 
   void Nonstandard(LanguageFeature lf, const MessageFixedText &msg) {
index 59ee680..12a1436 100644 (file)
@@ -100,15 +100,16 @@ struct GenericExprWrapper;  // forward definition, wraps Expr<SomeType>
 // Many classes below simply wrap a std::variant<> discriminated union,
 // which is conventionally named "u".
 #define UNION_CLASS_BOILERPLATE(classname) \
-  template<typename A> classname(A &&x) : u(std::move(x)) {} \
+  template<typename A, NOT_LVALUE_REFERENCE(A)> \
+  classname(A &&x) : u(std::move(x)) {} \
   using UnionTrait = std::true_type; \
   BOILERPLATE(classname)
 
 // Many other classes below simply wrap a std::tuple<> structure, which
 // is conventionally named "t".
 #define TUPLE_CLASS_BOILERPLATE(classname) \
-  template<typename... Ts> \
-  classname(Ts &&... args) : t(std::forward<Ts>(args)...) {} \
+  template<typename... Ts, bool = (... && !std::is_lvalue_reference_v<Ts>)> \
+  classname(Ts &&... args) : t(std::move(args)...) {} \
   using TupleTrait = std::true_type; \
   BOILERPLATE(classname)
 
index 219d12e..444bd7f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+// Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -71,7 +71,7 @@ public:
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
 
   template<typename... A> Message &Say(A... a) {
-    Message &m{messages_.Say(std::forward<A>(a)...)};
+    Message &m{messages_.Say(a...)};
     std::optional<ProvenanceRange> range{m.GetProvenanceRange(cooked_)};
     CHECK(!range.has_value() || cooked_.IsValid(*range));
     return m;
index cfc652e..603f93a 100644 (file)
@@ -121,7 +121,7 @@ private:
   MaskExpr GetMask(const parser::LogicalExpr &, bool defaultValue = true) const;
 
   template<typename... A> parser::Message *Say(A... args) {
-    return messages_.Say(std::forward<A>(args)...);
+    return messages_.Say(args...);
   }
 
   SemanticsContext &context_;
index 508d4ac..03bdd95 100644 (file)
@@ -56,7 +56,8 @@ using common::TypeCategory;
 
 // If a generic expression simply wraps a DataRef, extract it.
 // TODO: put in tools.h?
-template<typename A> std::optional<DataRef> ExtractDataRef(A &&) {
+template<typename A, NOT_LVALUE_REFERENCE(A)>
+std::optional<DataRef> ExtractDataRef(A &&) {
   return std::nullopt;
 }
 
@@ -164,13 +165,14 @@ static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
 // or FunctionRef<>) that has been instantiated on a dynamically chosen type.
 // TODO: move to tools.h?
 template<TypeCategory CATEGORY, template<typename> typename WRAPPER,
-    typename WRAPPED>
+    typename WRAPPED, NOT_LVALUE_REFERENCE(WRAPPED)>
 MaybeExpr WrapperHelper(int kind, WRAPPED &&x) {
   return common::SearchTypes(
       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
 }
 
-template<template<typename> typename WRAPPER, typename WRAPPED>
+template<template<typename> typename WRAPPER, typename WRAPPED,
+    NOT_LVALUE_REFERENCE(WRAPPED)>
 MaybeExpr TypedWrapper(const DynamicType &dyType, WRAPPED &&x) {
   switch (dyType.category) {
   case TypeCategory::Integer:
@@ -355,7 +357,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
 
 // A utility subroutine to repackage optional expressions of various levels
 // of type specificity as fully general MaybeExpr values.
-template<typename A> MaybeExpr AsMaybeExpr(A &&x) {
+template<typename A, NOT_LVALUE_REFERENCE(A)> MaybeExpr AsMaybeExpr(A &&x) {
   return std::make_optional(AsGenericExpr(std::move(x)));
 }
 template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
index 4aa68b6..604981c 100644 (file)
@@ -80,13 +80,14 @@ public:
     return GetFoldingContext().messages();
   }
 
-  template<typename... A> parser::Message *Say(A... args) {
-    return GetContextualMessages().Say(std::forward<A>(args)...);
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
+  parser::Message *Say(A &&... args) {
+    return GetContextualMessages().Say(std::move(args)...);
   }
 
-  template<typename T, typename... A>
-  parser::Message *SayAt(const T &parsed, A... args) {
-    return Say(parser::FindSourceLocation(parsed), std::forward<A>(args)...);
+  template<typename T, typename... A, NO_LVALUE_REFERENCE(A)>
+  parser::Message *SayAt(const T &parsed, A &&... args) {
+    return Say(parser::FindSourceLocation(parsed), std::move(args)...);
   }
 
   int GetDefaultKind(common::TypeCategory);
index cd1ee9d..bcb2277 100644 (file)
@@ -145,7 +145,7 @@ public:
   // It is not in any scope and always has MiscDetails.
   void MakePlaceholder(const parser::Name &, MiscDetails::Kind);
 
-  template<typename T> auto FoldExpr(T &&expr) -> T {
+  template<typename T, NOT_LVALUE_REFERENCE(T)> auto FoldExpr(T &&expr) -> T {
     return evaluate::Fold(GetFoldingContext(), std::move(expr));
   }
 
@@ -172,11 +172,12 @@ public:
     }
   }
 
-  template<typename... A> Message &Say(const parser::Name &name, A... args) {
-    return messageHandler_.Say(name.source, std::forward<A>(args)...);
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
+  Message &Say(const parser::Name &name, A &&... args) {
+    return messageHandler_.Say(name.source, std::move(args)...);
   }
-  template<typename... A> Message &Say(A... args) {
-    return messageHandler_.Say(std::forward<A>(args)...);
+  template<typename... A, NO_LVALUE_REFERENCE(A)> Message &Say(A &&... args) {
+    return messageHandler_.Say(std::move(args)...);
   }
 
 private:
@@ -443,18 +444,18 @@ public:
   Symbol &MakeSymbol(const SourceName &, Attrs = Attrs{});
   Symbol &MakeSymbol(const parser::Name &, Attrs = Attrs{});
 
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   Symbol &MakeSymbol(const parser::Name &name, D &&details) {
     return MakeSymbol(name, Attrs{}, std::move(details));
   }
 
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   Symbol &MakeSymbol(
       const parser::Name &name, const Attrs &attrs, D &&details) {
     return Resolve(name, MakeSymbol(name.source, attrs, std::move(details)));
   }
 
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   Symbol &MakeSymbol(const SourceName &name, const Attrs &attrs, D &&details) {
     // Note: don't use FindSymbol here. If this is a derived type scope,
     // we want to detect whether the name is already declared as a component.
@@ -2535,7 +2536,7 @@ void DeclarationVisitor::Post(const parser::CodimensionDecl &x) {
   const auto &name{std::get<parser::Name>(x.t)};
   DeclareObjectEntity(name, Attrs{});
 }
-//TODO: ChangeTeamStmt also uses CodimensionDecl
+// TODO: ChangeTeamStmt also uses CodimensionDecl
 
 void DeclarationVisitor::Post(const parser::EntityDecl &x) {
   // TODO: may be under StructureStmt
index 29f37d1..5148477 100644 (file)
@@ -107,12 +107,12 @@ public:
     return try_emplace(name, attrs, UnknownDetails());
   }
   /// Make a Symbol with provided details.
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   std::pair<iterator, bool> try_emplace(const SourceName &name, D &&details) {
-    return try_emplace(name, Attrs(), details);
+    return try_emplace(name, Attrs(), std::move(details));
   }
   /// Make a Symbol with attrs and details
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   std::pair<iterator, bool> try_emplace(
       const SourceName &name, Attrs attrs, D &&details) {
     Symbol &symbol{MakeSymbol(name, attrs, std::move(details))};
@@ -125,7 +125,7 @@ public:
   Symbol *FindCommonBlock(const SourceName &);
 
   /// Make a Symbol but don't add it to the scope.
-  template<typename D>
+  template<typename D, NOT_LVALUE_REFERENCE(D)>
   Symbol &MakeSymbol(const SourceName &name, Attrs attrs, D &&details) {
     return allSymbols.Make(*this, name, attrs, std::move(details));
   }
index 6fa3817..e802b96 100644 (file)
@@ -83,13 +83,14 @@ public:
 
   bool AnyFatalError() const;
 
-  template<typename... A>
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
   parser::Message &Say(const parser::CharBlock &at, A &&... args) {
-    return messages_.Say(at, std::forward<A>(args)...);
+    return messages_.Say(at, std::move(args)...);
   }
-  template<typename... A> parser::Message &Say(A &&... args) {
+  template<typename... A, NO_LVALUE_REFERENCE(A)>
+  parser::Message &Say(A &&... args) {
     CHECK(location_);
-    return messages_.Say(*location_, std::forward<A>(args)...);
+    return messages_.Say(*location_, std::move(args)...);
   }
   parser::Message &Say(parser::Message &&msg) {
     return messages_.Say(std::move(msg));