[flang] Remove OwningPointer, use unique_ptr better instead.

Original-commit: flang-compiler/f18@89aff868aa
Reviewed-on: https://github.com/flang-compiler/f18/pull/346
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-03-20 11:38:45 -07:00
parent 446890e74c
commit ad057a42fa
25 changed files with 167 additions and 260 deletions

View File

@ -92,9 +92,9 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-O2 '-DCHECK=(void)'")
set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUGF18")
# Building shared libraries is death on performance with GCC by default
# due to the need to preserve the right to override external entry points

View File

@ -192,20 +192,17 @@ will be defined to return a reference.)
wherever appropriate.
* `std::unique_ptr<>`: A nullable pointer with ownership, null by default,
not copyable, reassignable.
F18 has a helpful `Deleter<>` class template that makes `unique_ptr<>`
easier to use with forward-referenced data types.
* `std::shared_ptr<>`: A nullable pointer with shared ownership via reference
counting, null by default, shallowly copyable, reassignable, and slow.
* `OwningPointer<>`: A nullable pointer with ownership, better suited
for use with forward-defined types than `std::unique_ptr<>` is.
Null by default, optionally copyable, reassignable.
Does not have direct means for allocating data, and inconveniently requires
the definition of an external destructor.
* `Indirection<>`: A non-nullable pointer with ownership and
optional deep copy semantics; reassignable.
Often better than a reference (due to ownership) or `std::unique_ptr<>`
(due to non-nullability and copyability).
Can be wrapped in `std::optional<>` when nullability is required.
* `ForwardReference<>`: A non-nullable `OwningPointer<>`, or a variant of
`Indirection<>` that works with forward-declared content types; it's both.
Usable with forward-referenced data types with some use of `extern template`
in headers and explicit template instantiation in source files.
* `CountedReference<>`: A nullable pointer with shared ownership via
reference counting, null by default, shallowly copyable, reassignable.
Safe to use *only* when the data are private to just one
@ -219,11 +216,9 @@ A feature matrix:
| ------- | -------- | ------------ | ------ | ------------ | -------- | ------------------ |
| `*p` | yes | no | no | yes | shallowly | yes |
| `&r` | no | n/a | no | no | shallowly | yes |
| `unique_ptr<>` | yes | yes | yes | yes | no | no |
| `unique_ptr<>` | yes | yes | yes | yes | no | yes, with work |
| `shared_ptr<>` | yes | yes | yes | yes | shallowly | no |
| `OwningPointer<>` | yes | yes | yes | yes | optionally deeply | yes |
| `Indirection<>` | no | n/a | yes | yes | optionally deeply | no |
| `ForwardReference<>` | no | n/a | yes | yes | optionally deeply | yes |
| `Indirection<>` | no | n/a | yes | yes | optionally deeply | yes, with work |
| `CountedReference<>` | yes | yes | yes | yes | shallowly | no |
### Overall design preferences

View File

@ -25,9 +25,9 @@
namespace Fortran::FIR {
namespace {
Expression *ExprRef(const parser::Expr &a) { return &a.typedExpr.value(); }
Expression *ExprRef(const parser::Expr &a) { return a.typedExpr.get(); }
Expression *ExprRef(const common::Indirection<parser::Expr> &a) {
return &a.value().typedExpr.value();
return a.value().typedExpr.get();
}
struct LinearOp;
@ -1296,9 +1296,9 @@ public:
const std::vector<LinearLabelRef> &refs) {
auto &cases{
std::get<std::list<parser::CaseConstruct::Case>>(caseConstruct->t)};
SwitchCaseArguments result{
GetSwitchCaseSelector(caseConstruct), unspecifiedLabel,
populateSwitchValues(builder_, cases), std::move(refs)};
SwitchCaseArguments result{GetSwitchCaseSelector(caseConstruct),
unspecifiedLabel, populateSwitchValues(builder_, cases),
std::move(refs)};
cleanupSwitchPairs<SwitchCaseStmt>(
result.defLab, result.values, result.labels);
return result;

View File

@ -135,7 +135,6 @@ 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; }
}
#endif // FORTRAN_COMMON_IDIOMS_H_

View File

@ -15,20 +15,16 @@
#ifndef FORTRAN_COMMON_INDIRECTION_H_
#define FORTRAN_COMMON_INDIRECTION_H_
// Defines several smart pointer class templates that are rather like
// std::unique_ptr<>.
// - Indirection<> is, like a C++ reference type, restricted to be non-null
// when constructed or assigned.
// - OwningPointer<> is like a std::unique_ptr<> with an out-of-line destructor.
// This makes it suitable for use with forward-declared content types
// in a way that bare C pointers allow but std::unique_ptr<> cannot.
// - ForwardReference<> is a kind of Indirection<> that, like OwningPointer<>,
// accommodates the use of forward declarations.
// Users of Indirection<> and ForwardReference<> need to check whether their
// pointers are null. Like a C++ reference, they are meant to be as invisible
// as possible.
// All of these can optionally support copy construction
// and copy assignment.
// Define a smart pointer class template that is rather like
// non-nullable std::unique_ptr<>. Indirection<> is, like a C++ reference
// type, restricted to be non-null when constructed or assigned.
// Indirection<> optionally supports copy construction and copy assignment.
//
// To use Indirection<> with forward-referenced types, add
// extern template class Fortran::common::Indirection<FORWARD_TYPE>;
// outside any namespace in a header before use, and
// template class Fortran::common::Indirection<FORWARD_TYPE>;
// in one C++ source file later where a definition of the type is visible.
#include "../common/idioms.h"
#include <memory>
@ -66,8 +62,10 @@ public:
A &value() { return *p_; }
const A &value() const { return *p_; }
bool operator==(const A &x) const { return *p_ == x; }
bool operator==(const A &that) const { return *p_ == that; }
bool operator==(const Indirection &that) const { return *p_ == *that.p_; }
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)...)};
@ -117,8 +115,10 @@ public:
A &value() { return *p_; }
const A &value() const { return *p_; }
bool operator==(const A &x) const { return *p_ == x; }
bool operator==(const A &that) const { return *p_ == that; }
bool operator==(const Indirection &that) const { return *p_ == *that.p_; }
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)...)};
@ -128,148 +128,22 @@ private:
A *p_{nullptr};
};
// A variant of Indirection suitable for use with forward-referenced types.
// These are nullable pointers, not references. Allocation is not available,
// and a single externalized destructor must be defined. Copyable if an
// external copy constructor and operator= are implemented.
template<typename A> class OwningPointer {
template<typename A> using CopyableIndirection = Indirection<A, true>;
// For use with std::unique_ptr<> when declaring owning pointers to
// forward-referenced types, here's a minimal custom deleter that avoids
// some of the drama with std::default_delete<>. Invoke DEFINE_DELETER()
// later in exactly one C++ source file where a complete definition of the
// type is visible. Be advised, std::unique_ptr<> does not have copy
// semantics; if you need ownership, copy semantics, and nullability,
// std::optional<CopyableIndirection<>> works.
template<typename A> class Deleter {
public:
using element_type = A;
OwningPointer() {}
OwningPointer(OwningPointer &&that) : p_{that.p_} { that.p_ = nullptr; }
explicit OwningPointer(std::unique_ptr<A> &&that) : p_{that.release()} {}
explicit OwningPointer(A *&&p) : p_{p} { p = nullptr; }
// Must be externally defined; see DEFINE_OWNING_DESTRUCTOR below
~OwningPointer();
// Must be externally defined if copying is needed.
OwningPointer(const A &);
OwningPointer(const OwningPointer &);
OwningPointer &operator=(const A &);
OwningPointer &operator=(const OwningPointer &);
OwningPointer &operator=(OwningPointer &&that) {
auto tmp{p_};
p_ = that.p_;
that.p_ = tmp;
return *this;
}
OwningPointer &operator=(A *&&p) {
return *this = OwningPointer(std::move(p));
}
bool has_value() const { return p_ != nullptr; }
A &value() {
CHECK(p_ != nullptr);
return *p_;
}
const A &value() const {
CHECK(p_ != nullptr);
return *p_;
}
bool operator==(const A &x) const { return p_ != nullptr && *p_ == x; }
bool operator==(const OwningPointer &that) const {
return (p_ == nullptr && that.p_ == nullptr) ||
(that.p_ != nullptr && *this == *that.p_);
}
private:
A *p_{nullptr};
};
// ForwardReference can be viewed as either a non-nullable variant of
// OwningPointer or as a variant of Indirection that accommodates use with
// a forward-declared content type.
template<typename A> class ForwardReference {
public:
using element_type = A;
explicit ForwardReference(std::unique_ptr<A> &&that) : p_{that.release()} {}
explicit ForwardReference(A *&&p) : p_{p} {
CHECK(p_ && "assigning null pointer to ForwardReference");
p = nullptr;
}
ForwardReference(ForwardReference<A> &&that) : p_{that.p_} {
CHECK(p_ &&
"move construction of ForwardReference from null ForwardReference");
that.p_ = nullptr;
}
// Must be externally defined; see DEFINE_OWNING_DESTRUCTOR below
~ForwardReference();
// Must be externally defined if copying is needed.
ForwardReference(const A &);
ForwardReference(const ForwardReference &);
ForwardReference &operator=(const A &);
ForwardReference &operator=(const ForwardReference &);
ForwardReference &operator=(ForwardReference &&that) {
CHECK(that.p_ &&
"move assignment of null ForwardReference to ForwardReference");
auto tmp{p_};
p_ = that.p_;
that.p_ = tmp;
return *this;
}
ForwardReference &operator=(A *&&p) {
return *this = ForwardReference(std::move(p));
}
A &value() { return *p_; }
const A &value() const { return *p_; }
bool operator==(const A &x) const { return *p_ == x; }
bool operator==(const ForwardReference &that) const {
return *p_ == *that.p_;
}
private:
A *p_{nullptr};
void operator()(A *) const;
};
}
// Mandatory instantiation and definition -- put somewhere, not in a namespace
// CLASS here is OwningPointer or ForwardReference.
#define DEFINE_OWNING_DESTRUCTOR(CLASS, A) \
namespace Fortran::common { \
template class CLASS<A>; \
template<> CLASS<A>::~CLASS() { \
delete p_; \
p_ = nullptr; \
} \
#define DEFINE_DELETER(A) \
template<> void Fortran::common::Deleter<A>::operator()(A *p) const { \
delete p; \
}
// Optional definitions for OwningPointer and ForwardReference
#define DEFINE_OWNING_COPY_CONSTRUCTORS(CLASS, A) \
namespace Fortran::common { \
template<> CLASS<A>::CLASS(const A &that) : p_{new A(that)} {} \
template<> \
CLASS<A>::CLASS(const CLASS<A> &that) \
: p_{that.p_ ? new A(*that.p_) : nullptr} {} \
}
#define DEFINE_OWNING_COPY_ASSIGNMENTS(CLASS, A) \
namespace Fortran::common { \
template<> CLASS<A> &CLASS<A>::operator=(const A &that) { \
delete p_; \
p_ = new A(that); \
return *this; \
} \
template<> CLASS<A> &CLASS<A>::operator=(const CLASS<A> &that) { \
delete p_; \
p_ = that.p_ ? new A(*that.p_) : nullptr; \
return *this; \
} \
}
#define DEFINE_OWNING_COPY_FUNCTIONS(CLASS, A) \
DEFINE_OWNING_COPY_CONSTRUCTORS(CLASS, A) \
DEFINE_OWNING_COPY_ASSIGNMENTS(CLASS, A)
#define DEFINE_OWNING_SPECIAL_FUNCTIONS(CLASS, A) \
DEFINE_OWNING_DESTRUCTOR(CLASS, A) \
DEFINE_OWNING_COPY_FUNCTIONS(CLASS, A)
#endif // FORTRAN_COMMON_INDIRECTION_H_

View 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.
@ -70,10 +70,6 @@ template<typename A, typename... Bs>
auto Unwrap(const std::variant<Bs...> &) -> std::add_const_t<A> *;
template<typename A, typename B, bool COPY>
auto Unwrap(const Indirection<B, COPY> &) -> Constify<A, B> *;
template<typename A, typename B>
auto Unwrap(const OwningPointer<B> &) -> Constify<A, B> *;
template<typename A, typename B>
auto Unwrap(const CountedReference<B> &) -> Constify<A, B> *;
// Implementations of specializations
template<typename A, typename B> auto Unwrap(B *p) -> Constify<A, B> * {
@ -144,15 +140,6 @@ auto Unwrap(const Indirection<B, COPY> &p) -> Constify<A, B> * {
return Unwrap<A>(*p);
}
template<typename A, typename B>
auto Unwrap(const OwningPointer<B> &p) -> Constify<A, B> * {
if (p.get() != nullptr) {
return Unwrap<A>(*p);
} else {
return nullptr;
}
}
template<typename A, typename B>
auto Unwrap(const CountedReference<B> &p) -> Constify<A, B> * {
if (p.get() != nullptr) {

View File

@ -34,7 +34,7 @@ namespace Fortran::evaluate {
class ActualArgument {
public:
explicit ActualArgument(Expr<SomeType> &&x) : value_{std::move(x)} {}
explicit ActualArgument(CopyableIndirection<Expr<SomeType>> &&v)
explicit ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&v)
: value_{std::move(v)} {}
Expr<SomeType> &value() { return value_.value(); }
@ -57,7 +57,7 @@ private:
// e.g. between X and (X). The parser attempts to parse each argument
// first as a variable, then as an expression, and the distinction appears
// in the parse tree.
CopyableIndirection<Expr<SomeType>> value_;
common::CopyableIndirection<Expr<SomeType>> value_;
};
using ActualArguments = std::vector<std::optional<ActualArgument>>;

View File

@ -99,7 +99,4 @@ std::ostream &Procedure::Dump(std::ostream &o) const {
return o << (sep == '(' ? "()" : ")");
}
}
// Define OwningPointer special member functions
DEFINE_OWNING_SPECIAL_FUNCTIONS(
OwningPointer, evaluate::characteristics::Procedure)
DEFINE_DELETER(Fortran::evaluate::characteristics::Procedure)

View File

@ -54,7 +54,7 @@ struct DummyDataObject {
struct DummyProcedure {
ENUM_CLASS(Attr, Pointer, Optional)
DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
common::OwningPointer<Procedure> explicitProcedure;
std::unique_ptr<Procedure, common::Deleter<Procedure>> explicitProcedure;
common::EnumSet<Attr, 32> attrs;
bool operator==(const DummyProcedure &) const;
std::ostream &Dump(std::ostream &) const;

View File

@ -166,11 +166,21 @@ using HostUnsignedInt =
// - There are full copy and move semantics for construction and assignment.
// - Discriminated unions have a std::variant<> member "u" and support
// explicit copy and move constructors as well as comparison for equality.
#define DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
t(const t &); \
t(t &&); \
t &operator=(const t &); \
t &operator=(t &&);
#define DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
t(const t &) = default; \
t(t &&) = default; \
t &operator=(const t &) = default; \
t &operator=(t &&) = default;
#define DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
t::t(const t &) = default; \
t::t(t &&) = default; \
t &t::operator=(const t &) = default; \
t &t::operator=(t &&) = default;
#define CLASS_BOILERPLATE(t) \
t() = delete; \
@ -184,9 +194,6 @@ using HostUnsignedInt =
: u(std::move(x)) {} \
bool operator==(const t &that) const { return u == that.u; }
// Force availability of copy construction and assignment
template<typename A> using CopyableIndirection = common::Indirection<A, true>;
// Forward definition of Expr<> so that it can be indirectly used in its own
// definition
template<typename A> class Expr;

View File

@ -128,8 +128,8 @@ private:
std::vector<std::int64_t> shape_;
};
using StructureConstructorValues =
std::map<const semantics::Symbol *, CopyableIndirection<Expr<SomeType>>>;
using StructureConstructorValues = std::map<const semantics::Symbol *,
common::CopyableIndirection<Expr<SomeType>>>;
template<>
class Constant<SomeDerived>

View File

@ -51,10 +51,12 @@ public:
}
}
template<typename X> void Descend(const CopyableIndirection<X> &p) {
template<typename X, bool COPY>
void Descend(const common::Indirection<X, COPY> &p) {
Visit(p.value());
}
template<typename X> void Descend(CopyableIndirection<X> &p) {
template<typename X, bool COPY>
void Descend(common::Indirection<X, COPY> &p) {
Visit(p.value());
}

View File

@ -86,7 +86,8 @@ std::ostream &LogicalOperation<KIND>::Infix(std::ostream &o) const {
}
template<typename T>
std::ostream &Emit(std::ostream &o, const CopyableIndirection<Expr<T>> &expr) {
std::ostream &Emit(
std::ostream &o, const common::CopyableIndirection<Expr<T>> &expr) {
return expr.value().AsFortran(o);
}
@ -147,7 +148,7 @@ std::ostream &ExpressionBase<RESULT>::AsFortran(std::ostream &o) const {
o << "z'" << x.Hexadecimal() << "'";
},
[&](const NullPointer &) { o << "NULL()"; },
[&](const CopyableIndirection<Substring> &s) {
[&](const common::CopyableIndirection<Substring> &s) {
s.value().AsFortran(o);
},
[&](const ImpliedDoIndex &i) { o << i.name.ToString(); },
@ -184,7 +185,7 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
u);
}
Expr<SomeType>::~Expr() {}
Expr<SomeType>::~Expr() = default;
#if defined(__APPLE__) && defined(__GNUC__)
template<typename A>
@ -312,10 +313,28 @@ std::ostream &DerivedTypeSpecAsFortran(
return o;
}
GenericExprWrapper::~GenericExprWrapper() = default;
bool GenericExprWrapper::operator==(const GenericExprWrapper &that) const {
return v == that.v;
}
template<TypeCategory CAT> int Expr<SomeKind<CAT>>::GetKind() const {
return std::visit(
[](const auto &kx) { return std::decay_t<decltype(kx)>::Result::kind; },
u);
}
int Expr<SomeCharacter>::GetKind() const {
return std::visit(
[](const auto &kx) { return std::decay_t<decltype(kx)>::Result::kind; },
u);
}
Expr<SubscriptInteger> Expr<SomeCharacter>::LEN() const {
return std::visit([](const auto &kx) { return kx.LEN(); }, u);
}
// Template instantiations to resolve the "extern template" declarations
// that appear in expression.h.
@ -329,9 +348,4 @@ FOR_EACH_TYPE_AND_KIND(template class ExpressionBase)
FOR_EACH_INTRINSIC_KIND(template class ArrayConstructorValues)
FOR_EACH_INTRINSIC_KIND(template class ArrayConstructor)
}
// For reclamation of analyzed expressions to which owning pointers have
// been embedded in the parse tree. This destructor appears here, where
// definitions for all the necessary types are available, to obviate a
// need to include lib/evaluate/*.h headers in the parser proper.
DEFINE_OWNING_SPECIAL_FUNCTIONS(OwningPointer, evaluate::GenericExprWrapper)
DEFINE_DELETER(Fortran::evaluate::GenericExprWrapper)

View File

@ -124,9 +124,9 @@ public:
// Unary operations wrap a single Expr with a CopyableIndirection.
// Binary operations wrap a tuple of CopyableIndirections to Exprs.
private:
using Container =
std::conditional_t<operands == 1, CopyableIndirection<Expr<Operand<0>>>,
std::tuple<CopyableIndirection<Expr<OPERANDS>>...>>;
using Container = std::conditional_t<operands == 1,
common::CopyableIndirection<Expr<Operand<0>>>,
std::tuple<common::CopyableIndirection<Expr<OPERANDS>>...>>;
public:
CLASS_BOILERPLATE(Operation)
@ -437,14 +437,14 @@ public:
private:
parser::CharBlock name_;
CopyableIndirection<Expr<Index>> lower_, upper_, stride_;
CopyableIndirection<ArrayConstructorValues<Result>> values_;
common::CopyableIndirection<Expr<Index>> lower_, upper_, stride_;
common::CopyableIndirection<ArrayConstructorValues<Result>> values_;
};
template<typename RESULT> struct ArrayConstructorValue {
using Result = RESULT;
EVALUATE_UNION_CLASS_BOILERPLATE(ArrayConstructorValue)
std::variant<CopyableIndirection<Expr<Result>>, ImpliedDo<Result>> u;
std::variant<common::CopyableIndirection<Expr<Result>>, ImpliedDo<Result>> u;
};
template<typename RESULT> class ArrayConstructorValues {
@ -489,7 +489,7 @@ public:
const Expr<SubscriptInteger> &LEN() const { return length_.value(); }
private:
CopyableIndirection<Expr<SubscriptInteger>> length_;
common::CopyableIndirection<Expr<SubscriptInteger>> length_;
};
template<>
@ -747,14 +747,19 @@ class Expr<SomeKind<CAT>> : public ExpressionBase<SomeKind<CAT>> {
public:
using Result = SomeKind<CAT>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
int GetKind() const {
return std::visit(
[](const auto &x) { return std::decay_t<decltype(x)>::Result::kind; },
u);
}
int GetKind() const;
common::MapTemplate<Expr, CategoryTypes<CAT>> u;
};
template<> class Expr<SomeCharacter> : public ExpressionBase<SomeCharacter> {
public:
using Result = SomeCharacter;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
int GetKind() const;
Expr<SubscriptInteger> LEN() const;
common::MapTemplate<Expr, CategoryTypes<TypeCategory::Character>> u;
};
// BOZ literal "typeless" constants must be wide enough to hold a numeric
// value of any supported kind of INTEGER or REAL. They must also be
// distinguishable from other integer constants, since they are permitted
@ -807,10 +812,10 @@ public:
};
// This wrapper class is used, by means of a forward reference with
// OwningPointer, to implement owning pointers to analyzed expressions
// from parse tree nodes.
// an owning pointer, to cache analyzed expressions in parse tree nodes.
struct GenericExprWrapper {
GenericExprWrapper(Expr<SomeType> &&x) : v{std::move(x)} {}
~GenericExprWrapper();
bool operator==(const GenericExprWrapper &) const;
Expr<SomeType> v;
};

View File

@ -250,7 +250,7 @@ public:
}
private:
bool FoldArray(const CopyableIndirection<Expr<T>> &expr) {
bool FoldArray(const common::CopyableIndirection<Expr<T>> &expr) {
Expr<T> folded{Fold(context_, common::Clone(expr.value()))};
if (auto *c{UnwrapExpr<Constant<T>>(folded)}) {
// Copy elements in Fortran array element order

View File

@ -195,7 +195,7 @@ using SameKind = Type<CATEGORY, std::decay_t<T>::kind>;
// Many expressions, including subscripts, CHARACTER lengths, array bounds,
// and effective type parameter values, are of a maximal kind of INTEGER.
using IndirectSubscriptIntegerExpr =
CopyableIndirection<Expr<SubscriptInteger>>;
common::CopyableIndirection<Expr<SubscriptInteger>>;
// A predicate that is true when a kind value is a kind that could possibly
// be supported for an intrinsic type category on some target instruction

View File

@ -258,8 +258,8 @@ std::ostream &Emit(
return o;
}
template<typename A>
std::ostream &Emit(std::ostream &o, const CopyableIndirection<A> &p,
template<typename A, bool COPY>
std::ostream &Emit(std::ostream &o, const common::Indirection<A, COPY> &p,
const char *kw = nullptr) {
if (kw != nullptr) {
o << kw;

View File

@ -77,7 +77,7 @@ public:
CLASS_BOILERPLATE(Component)
Component(const DataRef &b, const Symbol &c) : base_{b}, symbol_{&c} {}
Component(DataRef &&b, const Symbol &c) : base_{std::move(b)}, symbol_{&c} {}
Component(CopyableIndirection<DataRef> &&b, const Symbol &c)
Component(common::CopyableIndirection<DataRef> &&b, const Symbol &c)
: base_{std::move(b)}, symbol_{&c} {}
const DataRef &base() const { return base_.value(); }
@ -90,7 +90,7 @@ public:
std::ostream &AsFortran(std::ostream &) const;
private:
CopyableIndirection<DataRef> base_;
common::CopyableIndirection<DataRef> base_;
const Symbol *symbol_;
};
@ -238,7 +238,7 @@ public:
private:
std::vector<const Symbol *> base_;
std::vector<Expr<SubscriptInteger>> subscript_, cosubscript_;
std::optional<CopyableIndirection<Expr<SomeInteger>>> stat_, team_;
std::optional<common::CopyableIndirection<Expr<SomeInteger>>> stat_, team_;
bool teamIsTeamNumber_{false}; // false: TEAM=, true: TEAM_NUMBER=
};

View File

@ -18,6 +18,13 @@
#include "../common/indirection.h"
#include <algorithm>
// So "delete Expr::typedExpr;" calls an external destructor.
namespace Fortran::evaluate {
struct GenericExprWrapper {
~GenericExprWrapper();
};
}
namespace Fortran::parser {
// R867
@ -171,3 +178,5 @@ std::ostream &operator<<(std::ostream &os, const CharBlock &x) {
return os << x.ToString();
}
}
template class std::unique_ptr<Fortran::evaluate::GenericExprWrapper>;

View File

@ -33,6 +33,7 @@
#include "../common/indirection.h"
#include <cinttypes>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
@ -66,14 +67,9 @@ class DerivedTypeSpec;
// Expressions in the parse tree have owning pointers that can be set to
// type-checked generic expression representations by semantic analysis.
// OwningPointer<> is used for leak safety without having to include
// the bulk of lib/evaluate/*.h headers into the parser proper.
namespace Fortran::evaluate {
struct GenericExprWrapper; // forward definition, wraps Expr<SomeType>
}
namespace Fortran::common {
extern template class OwningPointer<evaluate::GenericExprWrapper>;
}
// Most non-template classes in this file use these default definitions
// for their move constructor and move assignment operator=, and disable
@ -553,7 +549,7 @@ WRAPPER_CLASS(NamedConstant, Name);
// R1023 defined-binary-op -> . letter [letter]... .
// R1414 local-defined-operator -> defined-unary-op | defined-binary-op
// R1415 use-defined-operator -> defined-unary-op | defined-binary-op
// The Name here is stored with the dots; e.g., .FOO.
// The Name here is stored without the dots; e.g., FOO, not .FOO.
WRAPPER_CLASS(DefinedOpName, Name);
// R608 intrinsic-operator ->
@ -1695,8 +1691,10 @@ struct Expr {
explicit Expr(Designator &&);
explicit Expr(FunctionReference &&);
// Filled in after successful semantic analysis of the expression.
mutable common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
// Filled in with expression after successful semantic analysis.
mutable std::unique_ptr<evaluate::GenericExprWrapper,
common::Deleter<evaluate::GenericExprWrapper>>
typedExpr;
CharBlock source;
@ -2391,10 +2389,10 @@ struct ComputedGotoStmt {
};
// R1162 stop-code -> scalar-default-char-expr | scalar-int-expr
// We can't distinguish character expressions from integer
// expressions during parsing, so we just parse an expr and
// check its type later.
WRAPPER_CLASS(StopCode, Scalar<Expr>);
struct StopCode {
UNION_CLASS_BOILERPLATE(StopCode);
std::variant<ScalarDefaultCharExpr, ScalarIntExpr> u;
};
// R1160 stop-stmt -> STOP [stop-code] [, QUIET = scalar-logical-expr]
// R1161 error-stop-stmt ->
@ -2881,7 +2879,6 @@ struct GenericSpec {
EMPTY_CLASS(ReadUnformatted);
EMPTY_CLASS(WriteFormatted);
EMPTY_CLASS(WriteUnformatted);
CharBlock source;
std::variant<Name, DefinedOperator, Assignment, ReadFormatted,
ReadUnformatted, WriteFormatted, WriteUnformatted>
u;

View File

@ -17,6 +17,7 @@
#include "semantics.h"
#include "../common/indirection.h"
#include "../evaluate/expression.h"
namespace Fortran::parser {
template<typename> struct Statement;
@ -31,12 +32,23 @@ struct ForallStmt;
struct ForallConstruct;
}
namespace Fortran::evaluate {
void CheckPointerAssignment(parser::ContextualMessages &, const Symbol &,
const evaluate::Expr<evaluate::SomeType> &);
}
namespace Fortran::semantics {
class AssignmentContext;
}
extern template class Fortran::common::Indirection<
Fortran::semantics::AssignmentContext>;
namespace Fortran::semantics {
class AssignmentChecker : public virtual BaseChecker {
public:
explicit AssignmentChecker(SemanticsContext &);
~AssignmentChecker();
void Enter(const parser::AssignmentStmt &);
void Enter(const parser::PointerAssignmentStmt &);
void Enter(const parser::WhereStmt &);
@ -45,7 +57,7 @@ public:
void Enter(const parser::ForallConstruct &);
private:
common::ForwardReference<AssignmentContext> context_;
common::Indirection<AssignmentContext> context_;
};
// Semantic analysis of an assignment statement or WHERE/FORALL construct.
@ -62,6 +74,5 @@ void AnalyzeAssignment(
// well as in DO CONCURRENT loops.
void AnalyzeConcurrentHeader(
SemanticsContext &, const parser::ConcurrentHeader &);
}
#endif // FORTRAN_SEMANTICS_ASSIGNMENT_H_

View File

@ -304,13 +304,13 @@ static CS GatherLocalVariableNames(
static CS GatherReferencesFromExpression(const parser::Expr &expression) {
// Use the new expression traversal framework if possible, for testing.
if (expression.typedExpr.has_value()) {
if (expression.typedExpr) {
struct CollectSymbols : public virtual evaluate::VisitorBase<CS> {
explicit CollectSymbols(int) {}
void Handle(const Symbol *symbol) { result().push_back(symbol); }
};
return evaluate::Visitor<CS, CollectSymbols>{0}.Traverse(
expression.typedExpr.value());
*expression.typedExpr);
} else {
GatherSymbols gatherSymbols;
parser::Walk(expression, gatherSymbols);
@ -361,7 +361,8 @@ public:
auto &logicalExpr{
std::get<parser::ScalarLogicalExpr>(optionalLoopControl->u)
.thing.thing};
if (!ExpressionHasTypeCategory(logicalExpr.value().typedExpr.value(),
CHECK(logicalExpr.value().typedExpr);
if (!ExpressionHasTypeCategory(*logicalExpr.value().typedExpr,
common::TypeCategory::Logical)) {
messages_.Say(currentStatementSourcePosition_,
"DO WHILE must have LOGICAL expression"_err_en_US);
@ -528,16 +529,19 @@ private:
} // namespace Fortran::semantics
DEFINE_OWNING_DESTRUCTOR(ForwardReference, semantics::DoConcurrentContext)
namespace Fortran::semantics {
DoConcurrentChecker::DoConcurrentChecker(SemanticsContext &context)
: context_{new DoConcurrentContext{context}} {}
DoConcurrentChecker::~DoConcurrentChecker() = default;
// DO loops must be canonicalized prior to calling
void DoConcurrentChecker::Leave(const parser::DoConstruct &x) {
context_.value().Check(x);
}
} // namespace Fortran::semantics
template class Fortran::common::Indirection<
Fortran::semantics::DoConcurrentContext>;

View File

@ -21,17 +21,21 @@
namespace Fortran::parser {
struct DoConstruct;
}
namespace Fortran::semantics {
class DoConcurrentContext;
}
extern template class Fortran::common::Indirection<
Fortran::semantics::DoConcurrentContext>;
namespace Fortran::semantics {
class DoConcurrentChecker : public virtual BaseChecker {
public:
explicit DoConcurrentChecker(SemanticsContext &);
~DoConcurrentChecker();
void Leave(const parser::DoConstruct &);
private:
common::ForwardReference<DoConcurrentContext> context_;
common::Indirection<DoConcurrentContext> context_;
};
}

View 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.
@ -16,7 +16,7 @@
// Each is based on operator<< for that type. There are overloadings for
// reference and pointer, and for dumping to a provided ostream or cerr.
#ifdef DEBUG
#ifdef DEBUGF18
#include <iostream>

View File

@ -14,14 +14,16 @@
// The parse tree has slots in which pointers to typed expressions may be
// placed. When using the parser without the expression library, as here,
// we need to stub out the dependence.
// we need to stub out the dependence on the external destructor, which
// will never actually be called.
#include "../../lib/common/indirection.h"
namespace Fortran::evaluate {
struct GenericExprWrapper {
bool operator==(const GenericExprWrapper &) const { return false; }
~GenericExprWrapper();
};
GenericExprWrapper::~GenericExprWrapper() = default;
}
DEFINE_OWNING_DESTRUCTOR(OwningPointer, evaluate::GenericExprWrapper)
DEFINE_DELETER(Fortran::evaluate::GenericExprWrapper)