[flang] Redo without macros
authorpeter klausler <pklausler@nvidia.com>
Tue, 9 Apr 2019 20:29:40 +0000 (13:29 -0700)
committerpeter klausler <pklausler@nvidia.com>
Tue, 9 Apr 2019 20:29:40 +0000 (13:29 -0700)
Original-commit: flang-compiler/f18@8d955cfb3b6ac05066af34f574a76dd1bcd20d88
Reviewed-on: https://github.com/flang-compiler/f18/pull/390

21 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/common.h
flang/lib/evaluate/constant.h
flang/lib/evaluate/expression.h
flang/lib/evaluate/fold.cc
flang/lib/evaluate/integer.h
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/parse-tree.h
flang/lib/parser/token-parsers.h
flang/lib/semantics/expression.cc
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/scope.h
flang/lib/semantics/semantics.h

index cb09898..2a167b8 100644 (file)
@@ -95,14 +95,13 @@ template<typename... LAMBDAS> visitors(LAMBDAS... x)->visitors<LAMBDAS...>;
     template<typename A> std::false_type test(...); \
     template<typename A> \
     constexpr bool has_trait{decltype(test<A>(nullptr))::value}; \
-    template<typename A> \
-    constexpr typename std::enable_if_t<has_trait<A>, bool> trait_value() { \
-      using U = typename A::T; \
-      return U::value; \
-    } \
-    template<typename A> \
-    constexpr typename std::enable_if_t<!has_trait<A>, bool> trait_value() { \
-      return false; \
+    template<typename A> constexpr bool trait_value() { \
+      if constexpr (has_trait<A>) { \
+        using U = typename A::T; \
+        return U::value; \
+      } else { \
+        return false; \
+      } \
     } \
   } \
   template<typename A> constexpr bool T{class_trait_ns_##T::trait_value<A>()};
@@ -137,13 +136,19 @@ 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, NOT_LVALUE_REFERENCE(A)> void foo(A &&);
-// Works on parameter packs as well.
-#define NOT_LVALUE_REFERENCE(X) \
-  typename = std::enable_if_t<!std::is_lvalue_reference_v<X>, int>
-#define NO_LVALUE_REFERENCE(X) \
-  typename = std::enable_if_t<(... && !std::is_lvalue_reference_v<X>), int>
+// C++ does a weird and dangerous thing when deducing template type parameters
+// from function arguments: lvalue references are allowed to match rvalue
+// reference arguments.  Template function declarations like
+//   template<typename A> int foo(A &&);
+// need to be protected against this C++ language feature when functions
+// may modify such arguments.  Use these type functions to invoke SFINAE
+// on a result type via
+//   template<typename A> common::IfNoLvalue<int, A> foo(A &&);
+// or, for constructors,
+//   template<typename A, typename = common::NoLvalue<A>> int foo(A &&);
+// This works with parameter packs too.
+template<typename A, typename... B>
+using IfNoLvalue = std::enable_if_t<(... && !std::is_lvalue_reference_v<B>), A>;
+template<typename... RVREF> using NoLvalue = IfNoLvalue<void, RVREF...>;
 }
 #endif  // FORTRAN_COMMON_IDIOMS_H_
index 8c9dde5..b09cc3d 100644 (file)
@@ -67,8 +67,8 @@ public:
   bool operator!=(const A &that) const { return *p_ != that; }
   bool operator!=(const Indirection &that) const { return *p_ != *that.p_; }
 
-  template<typename... ARGS, NO_LVALUE_REFERENCE(ARGS)>
-  static Indirection Make(ARGS &&... args) {
+  template<typename... ARGS>
+  static common::IfNoLvalue<Indirection, ARGS...> Make(ARGS &&... args) {
     return {new A(std::move(args)...)};
   }
 
@@ -121,8 +121,8 @@ public:
   bool operator!=(const A &that) const { return *p_ != that; }
   bool operator!=(const Indirection &that) const { return *p_ != *that.p_; }
 
-  template<typename... ARGS, NO_LVALUE_REFERENCE(ARGS)>
-  static Indirection Make(ARGS &&... args) {
+  template<typename... ARGS>
+  static common::IfNoLvalue<Indirection, ARGS...> Make(ARGS &&... args) {
     return {new A(std::move(args)...)};
   }
 
index 92908b1..8b8c2d4 100644 (file)
@@ -36,8 +36,8 @@ private:
   A original_;
 };
 
-template<typename A, typename B, NOT_LVALUE_REFERENCE(B)>
-Restorer<A> ScopedSet(A &to, B &&from) {
+template<typename A, typename B>
+common::IfNoLvalue<Restorer<A>, B> ScopedSet(A &to, B &&from) {
   Restorer<A> result{to};
   to = std::move(from);
   return result;
index 8f5647c..42ba326 100644 (file)
@@ -119,8 +119,8 @@ template<typename TOV, typename FROMV> TOV CopyVariant(const FROMV &u) {
 
 // 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, NOT_LVALUE_REFERENCE(FROMV)>
-TOV MoveVariant(FROMV &&u) {
+template<typename TOV, typename FROMV>
+common::IfNoLvalue<TOV, FROMV> MoveVariant(FROMV &&u) {
   return std::visit(
       [](auto &&x) -> TOV { return {std::move(x)}; }, std::move(u));
 }
@@ -296,8 +296,9 @@ 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, NOT_LVALUE_REFERENCE(VISITOR)>
-typename VISITOR::Result SearchTypesHelper(VISITOR &&visitor) {
+template<std::size_t J, typename VISITOR>
+common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
+    VISITOR &&visitor) {
   using Tuple = typename VISITOR::Types;
   if constexpr (J < std::tuple_size_v<Tuple>) {
     if (auto result{visitor.template Test<std::tuple_element_t<J, Tuple>>()}) {
@@ -309,8 +310,9 @@ typename VISITOR::Result SearchTypesHelper(VISITOR &&visitor) {
   }
 }
 
-template<typename VISITOR, NOT_LVALUE_REFERENCE(VISITOR)>
-typename VISITOR::Result SearchTypes(VISITOR &&visitor) {
+template<typename VISITOR>
+common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes(
+    VISITOR &&visitor) {
   return SearchTypesHelper<0, VISITOR>(std::move(visitor));
 }
 }
index a8a778c..2c00802 100644 (file)
@@ -190,7 +190,7 @@ using HostUnsignedInt =
 #define EVALUATE_UNION_CLASS_BOILERPLATE(t) \
   CLASS_BOILERPLATE(t) \
   template<typename _A> explicit t(const _A &x) : u{x} {} \
-  template<typename _A, NOT_LVALUE_REFERENCE(_A)> \
+  template<typename _A, typename = common::NoLvalue<_A>> \
   explicit t(_A &&x) : u(std::move(x)) {} \
   bool operator==(const t &that) const { return u == that.u; }
 
index 80757f3..4ef0fd8 100644 (file)
@@ -43,7 +43,7 @@ public:
   using ScalarValue = SCALAR;
 
   template<typename A> ConstantBase(const A &x) : values_{x} {}
-  template<typename A, NOT_LVALUE_REFERENCE(A)>
+  template<typename A, typename = common::NoLvalue<A>>
   ConstantBase(A &&x) : values_{std::move(x)} {}
   ConstantBase(std::vector<ScalarValue> &&x, std::vector<std::int64_t> &&dims)
     : values_(std::move(x)), shape_(std::move(dims)) {}
index 6e3b9d6..e48bf0f 100644 (file)
@@ -86,7 +86,7 @@ public:
     return d;
   }
 
-  template<typename A, NOT_LVALUE_REFERENCE(A)> Derived &operator=(A &&x) {
+  template<typename A> common::IfNoLvalue<Derived &, A> operator=(A &&x) {
     Derived &d{derived()};
     d.u = std::move(x);
     return d;
@@ -452,7 +452,7 @@ public:
   ArrayConstructorValues() {}
   bool operator==(const ArrayConstructorValues &) const;
   static constexpr int Rank() { return 1; }
-  template<typename A, NOT_LVALUE_REFERENCE(A)> void Push(A &&x) {
+  template<typename A> common::NoLvalue<A> Push(A &&x) {
     values_.emplace_back(std::move(x));
   }
   Values &values() { return values_; }
@@ -520,10 +520,6 @@ public:
   using Result = Type<TypeCategory::Integer, KIND>;
 
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
-  explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
-  template<typename INT>
-  explicit Expr(std::enable_if_t<std::is_integral_v<INT>, INT> n)
-    : u{Constant<Result>{n}} {}
 
 private:
   using Conversions = std::tuple<Convert<Result, TypeCategory::Integer>,
index bc1074f..44d5831 100644 (file)
@@ -39,8 +39,9 @@
 namespace Fortran::evaluate {
 
 // no-op base case
-template<typename A, NOT_LVALUE_REFERENCE(A)>
-Expr<ResultType<A>> FoldOperation(FoldingContext &, A &&x) {
+template<typename A>
+common::IfNoLvalue<Expr<ResultType<A>>, A> FoldOperation(
+    FoldingContext &, A &&x) {
   return Expr<ResultType<A>>{std::move(x)};
 }
 
@@ -985,8 +986,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
 
 // Unary operations
 
-template<typename TO, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
-std::optional<TO> ConvertString(FROM &&s) {
+template<typename TO, typename FROM>
+common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(FROM &&s) {
   if constexpr (std::is_same_v<TO, FROM>) {
     return std::make_optional<TO>(std::move(s));
   } else {
index d1a5b6e..e48ace0 100644 (file)
@@ -122,8 +122,7 @@ public:
 
   // C++'s integral types can all be converted to Integer
   // with silent truncation.
-  template<typename INT,
-      typename = std::enable_if_t<std::is_integral_v<INT>, int>>
+  template<typename INT, typename = std::enable_if_t<std::is_integral_v<INT>>>
   constexpr Integer(INT n) {
     constexpr int nBits = CHAR_BIT * sizeof n;
     if constexpr (nBits < partBits) {
index 73feda8..aebbf50 100644 (file)
@@ -96,8 +96,7 @@ 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, NOT_LVALUE_REFERENCE(A)>
-Expr<ResultType<A>> AsExpr(A &&x) {
+template<typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) {
   return Expr<ResultType<A>>{std::move(x)};
 }
 
@@ -111,13 +110,14 @@ Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
   return std::move(x);
 }
 
-template<typename A, NOT_LVALUE_REFERENCE(A)>
-Expr<SomeType> AsGenericExpr(A &&x) {
+template<typename A>
+common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
   return Expr<SomeType>{AsCategoryExpr(std::move(x))};
 }
 
-template<typename A, NOT_LVALUE_REFERENCE(A)>
-Expr<SomeKind<ResultType<A>::category>> AsCategoryExpr(A &&x) {
+template<typename A>
+common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr(
+    A &&x) {
   return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
 }
 
@@ -239,13 +239,15 @@ std::optional<Expr<SomeType>> ConvertToType(
     const semantics::Symbol &, Expr<SomeType> &&);
 
 // Conversions to the type of another expression
-template<TypeCategory TC, int TK, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
-Expr<Type<TC, TK>> ConvertTo(const Expr<Type<TC, TK>> &, FROM &&x) {
+template<TypeCategory TC, int TK, typename FROM>
+common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
+    const Expr<Type<TC, TK>> &, FROM &&x) {
   return ConvertToType<Type<TC, TK>>(std::move(x));
 }
 
-template<TypeCategory TC, typename FROM, NOT_LVALUE_REFERENCE(FROM)>
-Expr<SomeKind<TC>> ConvertTo(const Expr<SomeKind<TC>> &to, FROM &&from) {
+template<TypeCategory TC, typename FROM>
+common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
+    const Expr<SomeKind<TC>> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toKindExpr) {
         using KindExpr = std::decay_t<decltype(toKindExpr)>;
@@ -255,8 +257,9 @@ Expr<SomeKind<TC>> ConvertTo(const Expr<SomeKind<TC>> &to, FROM &&from) {
       to.u);
 }
 
-template<typename FROM, NOT_LVALUE_REFERENCE(FROM)>
-Expr<SomeType> ConvertTo(const Expr<SomeType> &to, FROM &&from) {
+template<typename FROM>
+common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
+    const Expr<SomeType> &to, FROM &&from) {
   return std::visit(
       [&](const auto &toCatExpr) {
         return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
@@ -281,8 +284,9 @@ template<TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
   VALUE value;
 };
 
-template<TypeCategory TOCAT, typename VALUE, NOT_LVALUE_REFERENCE(VALUE)>
-Expr<SomeKind<TOCAT>> ConvertToKind(int kind, VALUE &&x) {
+template<TypeCategory TOCAT, typename VALUE>
+common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
+    int kind, VALUE &&x) {
   return common::SearchTypes(
       ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})
       .value();
index 1143fbc..ec5debe 100644 (file)
@@ -142,12 +142,12 @@ private:
 
 class RewriterBase {
 public:
-  template<typename A, NOT_LVALUE_REFERENCE(A)> A Handle(A &&x) {
+  template<typename A> common::IfNoLvalue<A, A> Handle(A &&x) {
     defaultHandleCalled_ = true;
     return std::move(x);
   }
   template<typename A> void Pre(const A &) {}
-  template<typename A, NOT_LVALUE_REFERENCE(A)> A Post(A &&x) {
+  template<typename A> common::IfNoLvalue<A, A> Post(A &&x) {
     return std::move(x);
   }
 
@@ -170,7 +170,7 @@ private:
   using RewriterBase::done_, RewriterBase::defaultHandleCalled_;
 
 public:
-  template<typename B, NOT_LVALUE_REFERENCE(B)> B Traverse(B &&x) {
+  template<typename B> common::IfNoLvalue<B, B> Traverse(B &&x) {
     if (!done_) {
       defaultHandleCalled_ = false;
       x = Handle(std::move(x));
index 9e193c4..aef2d46 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, NOT_LVALUE_REFERENCE(A)> Subscript &emplace_back(A &&x) {
+  template<typename A> common::IfNoLvalue<Subscript &, A> emplace_back(A &&x) {
     return subscript_.emplace_back(std::move(x));
   }
 
index a322cd2..7b284a4 100644 (file)
@@ -1192,8 +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, NOT_LVALUE_REFERENCE(T)>
-std::list<T> prepend(T &&head, std::list<T> &&rest) {
+template<typename T>
+common::IfNoLvalue<std::list<T>, T> prepend(T &&head, std::list<T> &&rest) {
   rest.push_front(std::move(head));
   return std::move(rest);
 }
index d8e5f15..d9613bb 100644 (file)
@@ -122,10 +122,10 @@ struct FormatItem {
   FormatItem() = delete;
   FormatItem(FormatItem &&) = default;
   FormatItem &operator=(FormatItem &&) = default;
-  template<typename A, NOT_LVALUE_REFERENCE(A)>
+  template<typename A, typename = common::NoLvalue<A>>
   FormatItem(std::optional<std::uint64_t> &&r, A &&x)
     : repeatCount{std::move(r)}, u{std::move(x)} {}
-  template<typename A, NOT_LVALUE_REFERENCE(A)>
+  template<typename A, typename = common::NoLvalue<A>>
   explicit FormatItem(A &&x) : u{std::move(x)} {}
   std::optional<std::uint64_t> repeatCount;
   std::variant<IntrinsicTypeDataEditDesc, DerivedTypeDataEditDesc,
index d33d45c..6fdd551 100644 (file)
@@ -2669,8 +2669,7 @@ TYPE_PARSER(first(construct<WaitSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
     construct<WaitSpec>("IOMSG =" >> msgVariable),
     construct<WaitSpec>("IOSTAT =" >> statVariable)))
 
-template<typename A, NOT_LVALUE_REFERENCE(A)>
-std::list<A> singletonList(A &&x) {
+template<typename A> common::IfNoLvalue<std::list<A>, A> singletonList(A &&x) {
   std::list<A> result;
   result.push_front(std::move(x));
   return result;
index f8828a6..1d2e1d1 100644 (file)
@@ -100,7 +100,7 @@ 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, NOT_LVALUE_REFERENCE(A)> \
+  template<typename A, typename = common::NoLvalue<A>> \
   classname(A &&x) : u(std::move(x)) {} \
   using UnionTrait = std::true_type; \
   BOILERPLATE(classname)
@@ -108,7 +108,7 @@ struct GenericExprWrapper;  // forward definition, wraps Expr<SomeType>
 // Many other classes below simply wrap a std::tuple<> structure, which
 // is conventionally named "t".
 #define TUPLE_CLASS_BOILERPLATE(classname) \
-  template<typename... Ts, NO_LVALUE_REFERENCE(Ts)> \
+  template<typename... Ts, typename = common::NoLvalue<Ts...>> \
   classname(Ts &&... args) : t(std::move(args)...) {} \
   using TupleTrait = std::true_type; \
   BOILERPLATE(classname)
index 51e1014..e3f0d5c 100644 (file)
@@ -184,15 +184,17 @@ constexpr TokenStringMatch operator""_sptok(const char str[], std::size_t n) {
   return TokenStringMatch{str, n, true};
 }
 
-template<class PA, std::enable_if_t<std::is_class<PA>::value, int> = 0>
-inline constexpr SequenceParser<TokenStringMatch, PA> operator>>(
-    const char *str, const PA &p) {
+template<class PA>
+inline constexpr std::enable_if_t<std::is_class_v<PA>,
+    SequenceParser<TokenStringMatch, PA>>
+operator>>(const char *str, const PA &p) {
   return SequenceParser<TokenStringMatch, PA>{TokenStringMatch{str, false}, p};
 }
 
-template<class PA, std::enable_if_t<std::is_class<PA>::value, int> = 0>
-inline constexpr InvertedSequenceParser<PA, TokenStringMatch> operator/(
-    const PA &p, const char *str) {
+template<class PA>
+inline constexpr std::enable_if_t<std::is_class_v<PA>,
+    InvertedSequenceParser<PA, TokenStringMatch>>
+operator/(const PA &p, const char *str) {
   return InvertedSequenceParser<PA, TokenStringMatch>{
       p, TokenStringMatch{str, false}};
 }
index 03bdd95..5c3e999 100644 (file)
@@ -56,8 +56,8 @@ using common::TypeCategory;
 
 // If a generic expression simply wraps a DataRef, extract it.
 // TODO: put in tools.h?
-template<typename A, NOT_LVALUE_REFERENCE(A)>
-std::optional<DataRef> ExtractDataRef(A &&) {
+template<typename A>
+common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(A &&) {
   return std::nullopt;
 }
 
@@ -165,15 +165,15 @@ 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, NOT_LVALUE_REFERENCE(WRAPPED)>
-MaybeExpr WrapperHelper(int kind, WRAPPED &&x) {
+    typename WRAPPED>
+common::IfNoLvalue<MaybeExpr, WRAPPED> WrapperHelper(int kind, WRAPPED &&x) {
   return common::SearchTypes(
       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
 }
 
-template<template<typename> typename WRAPPER, typename WRAPPED,
-    NOT_LVALUE_REFERENCE(WRAPPED)>
-MaybeExpr TypedWrapper(const DynamicType &dyType, WRAPPED &&x) {
+template<template<typename> typename WRAPPER, typename WRAPPED>
+common::IfNoLvalue<MaybeExpr, WRAPPED> TypedWrapper(
+    const DynamicType &dyType, WRAPPED &&x) {
   switch (dyType.category) {
   case TypeCategory::Integer:
     return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
@@ -357,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, NOT_LVALUE_REFERENCE(A)> MaybeExpr AsMaybeExpr(A &&x) {
+template<typename A> common::IfNoLvalue<MaybeExpr, A> AsMaybeExpr(A &&x) {
   return std::make_optional(AsGenericExpr(std::move(x)));
 }
 template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
index c1305b8..7a68f34 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, NOT_LVALUE_REFERENCE(T)> auto FoldExpr(T &&expr) -> T {
+  template<typename T> common::IfNoLvalue<T, T> FoldExpr(T &&expr) {
     return evaluate::Fold(GetFoldingContext(), std::move(expr));
   }
 
@@ -445,19 +445,21 @@ public:
   Symbol &MakeSymbol(const SourceName &, Attrs = Attrs{});
   Symbol &MakeSymbol(const parser::Name &, Attrs = Attrs{});
 
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  Symbol &MakeSymbol(const parser::Name &name, D &&details) {
+  template<typename D>
+  common::IfNoLvalue<Symbol &, D> MakeSymbol(
+      const parser::Name &name, D &&details) {
     return MakeSymbol(name, Attrs{}, std::move(details));
   }
 
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  Symbol &MakeSymbol(
+  template<typename D>
+  common::IfNoLvalue<Symbol &, D> MakeSymbol(
       const parser::Name &name, const Attrs &attrs, D &&details) {
     return Resolve(name, MakeSymbol(name.source, attrs, std::move(details)));
   }
 
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  Symbol &MakeSymbol(const SourceName &name, const Attrs &attrs, D &&details) {
+  template<typename D>
+  common::IfNoLvalue<Symbol &, D> 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.
     auto *symbol{FindInScope(currScope(), name)};
index 5148477..ee22578 100644 (file)
@@ -107,13 +107,14 @@ public:
     return try_emplace(name, attrs, UnknownDetails());
   }
   /// Make a Symbol with provided details.
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  std::pair<iterator, bool> try_emplace(const SourceName &name, D &&details) {
+  template<typename D>
+  common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
+      const SourceName &name, D &&details) {
     return try_emplace(name, Attrs(), std::move(details));
   }
   /// Make a Symbol with attrs and details
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  std::pair<iterator, bool> try_emplace(
+  template<typename D>
+  common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
       const SourceName &name, Attrs attrs, D &&details) {
     Symbol &symbol{MakeSymbol(name, attrs, std::move(details))};
     return symbols_.emplace(name, &symbol);
@@ -125,8 +126,9 @@ public:
   Symbol *FindCommonBlock(const SourceName &);
 
   /// Make a Symbol but don't add it to the scope.
-  template<typename D, NOT_LVALUE_REFERENCE(D)>
-  Symbol &MakeSymbol(const SourceName &name, Attrs attrs, D &&details) {
+  template<typename D>
+  common::IfNoLvalue<Symbol &, D> MakeSymbol(
+      const SourceName &name, Attrs attrs, D &&details) {
     return allSymbols.Make(*this, name, attrs, std::move(details));
   }
 
index e802b96..d99b76e 100644 (file)
@@ -83,12 +83,13 @@ public:
 
   bool AnyFatalError() const;
 
-  template<typename... A, NO_LVALUE_REFERENCE(A)>
-  parser::Message &Say(const parser::CharBlock &at, A &&... args) {
+  template<typename... A>
+  common::IfNoLvalue<parser::Message &, A...> Say(
+      const parser::CharBlock &at, A &&... args) {
     return messages_.Say(at, std::move(args)...);
   }
-  template<typename... A, NO_LVALUE_REFERENCE(A)>
-  parser::Message &Say(A &&... args) {
+  template<typename... A>
+  common::IfNoLvalue<parser::Message &, A...> Say(A &&... args) {
     CHECK(location_);
     return messages_.Say(*location_, std::move(args)...);
   }