// 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_
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:
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:
#ifndef FORTRAN_COMMON_RESTORER_H_
#define FORTRAN_COMMON_RESTORER_H_
+#include "idioms.h"
namespace Fortran::common {
template<typename A> class Restorer {
public:
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;
// 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));
}
// 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)...)}) {
}
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)...);
}
// 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>) {
}
}
-template<typename VISITOR>
+template<typename VISITOR, NOT_LVALUE_REFERENCE(VISITOR)>
typename VISITOR::Result SearchTypes(VISITOR &&visitor) {
return SearchTypesHelper<0, VISITOR>(std::move(visitor));
}
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;
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); }
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_; }
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)};
}
// 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 {
// 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)};
}
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))};
}
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) {
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) {
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)})
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; }
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));
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));
}
-// 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.
// 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);
}
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>>
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;
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_; }
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;
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_;
}
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:
-// 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.
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) {
// 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)
-// 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.
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;
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_;
// 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;
}
// 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:
// 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) {
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);
// 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));
}
}
}
- 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:
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.
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
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))};
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));
}
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));