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>()};
// 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_
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)...)};
}
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)...)};
}
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;
// 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));
}
// 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>>()}) {
}
}
-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));
}
}
#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; }
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)) {}
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;
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_; }
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>,
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)};
}
// 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 {
// 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) {
// 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)};
}
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))};
}
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)>;
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)));
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();
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);
}
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));
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));
}
// 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);
}
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,
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;
// 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)
// 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)
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}};
}
// 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;
}
// 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>(
// 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) {
// 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));
}
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)};
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);
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));
}
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)...);
}