forked from OSchip/llvm-project
[flang] Generalize tools, clean up common/unwrap.h with new-found knowledge
Original-commit: flang-compiler/f18@aac16907cd Reviewed-on: https://github.com/flang-compiler/f18/pull/416 Tree-same-pre-rewrite: false
This commit is contained in:
parent
bfb65b5476
commit
f79e3dd0b9
|
@ -46,107 +46,103 @@ template<typename A, typename B>
|
|||
using Constify = std::conditional_t<std::is_const_v<B> && !std::is_const_v<A>,
|
||||
std::add_const_t<A>, A>;
|
||||
|
||||
// Base case
|
||||
// Unwrap's mutually-recursive template functions are packaged in a struct
|
||||
// to avoid a need for prototypes.
|
||||
struct UnwrapperHelper {
|
||||
|
||||
// Base case
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(B &x) -> Constify<A, B> * {
|
||||
if constexpr (std::is_same_v<std::decay_t<A>, std::decay_t<B>>) {
|
||||
return &x;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations of specializations
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(B *p) -> Constify<A, B> * {
|
||||
if (p != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(const std::unique_ptr<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(const std::shared_ptr<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(std::optional<B> &x) -> Constify<A, B> * {
|
||||
if (x.has_value()) {
|
||||
return Unwrap<A>(*x);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(const std::optional<B> &x) -> Constify<A, B> * {
|
||||
if (x.has_value()) {
|
||||
return Unwrap<A>(*x);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename... Bs>
|
||||
static A *Unwrap(std::variant<Bs...> &u) {
|
||||
return std::visit(
|
||||
[](auto &x) -> A * {
|
||||
using Ty = std::decay_t<decltype(Unwrap<A>(x))>;
|
||||
if constexpr (!std::is_const_v<std::remove_pointer_t<Ty>> ||
|
||||
std::is_const_v<A>) {
|
||||
return Unwrap<A>(x);
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template<typename A, typename... Bs>
|
||||
static auto Unwrap(const std::variant<Bs...> &u) -> std::add_const_t<A> * {
|
||||
return std::visit(
|
||||
[](const auto &x) -> std::add_const_t<A> * { return Unwrap<A>(x); }, u);
|
||||
}
|
||||
|
||||
template<typename A, typename B, bool COPY>
|
||||
static auto Unwrap(const Indirection<B, COPY> &p) -> Constify<A, B> * {
|
||||
return Unwrap<A>(*p);
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static auto Unwrap(const CountedReference<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename A, typename B> auto Unwrap(B &x) -> Constify<A, B> * {
|
||||
if constexpr (std::is_same_v<std::decay_t<A>, std::decay_t<B>>) {
|
||||
return &x;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Prototypes of specializations, to enable mutual recursion
|
||||
template<typename A, typename B> auto Unwrap(B *p) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::unique_ptr<B> &) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::shared_ptr<B> &) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(std::optional<B> &) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::optional<B> &) -> std::add_const_t<A> *;
|
||||
template<typename A, typename... Bs> A *Unwrap(std::variant<Bs...> &);
|
||||
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> *;
|
||||
|
||||
// Implementations of specializations
|
||||
template<typename A, typename B> auto Unwrap(B *p) -> Constify<A, B> * {
|
||||
if (p != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::unique_ptr<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::shared_ptr<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(std::optional<B> &x) -> Constify<A, B> * {
|
||||
if (x.has_value()) {
|
||||
return Unwrap<A>(*x);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::optional<B> &x) -> Constify<A, B> * {
|
||||
if (x.has_value()) {
|
||||
return Unwrap<A>(*x);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename... Bs> A *Unwrap(std::variant<Bs...> &u) {
|
||||
return std::visit(
|
||||
[](auto &x) -> A * {
|
||||
using Ty = std::decay_t<decltype(Unwrap<A>(x))>;
|
||||
if constexpr (!std::is_const_v<std::remove_pointer_t<Ty>> ||
|
||||
std::is_const_v<A>) {
|
||||
return Unwrap<A>(x);
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template<typename A, typename... Bs>
|
||||
auto Unwrap(const std::variant<Bs...> &u) -> std::add_const_t<A> * {
|
||||
return std::visit(
|
||||
[](const auto &x) -> std::add_const_t<A> * { return Unwrap<A>(x); }, u);
|
||||
}
|
||||
|
||||
template<typename A, typename B, bool COPY>
|
||||
auto Unwrap(const Indirection<B, COPY> &p) -> Constify<A, B> * {
|
||||
return Unwrap<A>(*p);
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const CountedReference<B> &p) -> Constify<A, B> * {
|
||||
if (p.get() != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return UnwrapperHelper::Unwrap<A>(x);
|
||||
}
|
||||
|
||||
// Returns a copy of a wrapped value, if present, otherwise a vacant optional.
|
||||
|
|
|
@ -26,6 +26,7 @@ add_library(FortranParser
|
|||
provenance.cc
|
||||
source.cc
|
||||
token-sequence.cc
|
||||
tools.cc
|
||||
unparse.cc
|
||||
user-state.cc
|
||||
)
|
||||
|
|
|
@ -55,6 +55,7 @@ CLASS_TRAIT(EmptyTrait)
|
|||
CLASS_TRAIT(WrapperTrait)
|
||||
CLASS_TRAIT(UnionTrait)
|
||||
CLASS_TRAIT(TupleTrait)
|
||||
CLASS_TRAIT(ConstraintTrait)
|
||||
|
||||
// Some parse tree nodes have fields in them to cache the results of a
|
||||
// successful semantic analysis later. Their types are forward declared
|
||||
|
@ -272,6 +273,7 @@ using Location = const char *;
|
|||
// These template class wrappers correspond to the Standard's modifiers
|
||||
// scalar-xyz, constant-xzy, int-xzy, default-char-xyz, & logical-xyz.
|
||||
template<typename A> struct Scalar {
|
||||
using ConstraintTrait = std::true_type;
|
||||
Scalar(Scalar &&that) = default;
|
||||
Scalar(A &&that) : thing(std::move(that)) {}
|
||||
Scalar &operator=(Scalar &&) = default;
|
||||
|
@ -279,6 +281,7 @@ template<typename A> struct Scalar {
|
|||
};
|
||||
|
||||
template<typename A> struct Constant {
|
||||
using ConstraintTrait = std::true_type;
|
||||
Constant(Constant &&that) = default;
|
||||
Constant(A &&that) : thing(std::move(that)) {}
|
||||
Constant &operator=(Constant &&) = default;
|
||||
|
@ -286,6 +289,7 @@ template<typename A> struct Constant {
|
|||
};
|
||||
|
||||
template<typename A> struct Integer {
|
||||
using ConstraintTrait = std::true_type;
|
||||
Integer(Integer &&that) = default;
|
||||
Integer(A &&that) : thing(std::move(that)) {}
|
||||
Integer &operator=(Integer &&) = default;
|
||||
|
@ -293,6 +297,7 @@ template<typename A> struct Integer {
|
|||
};
|
||||
|
||||
template<typename A> struct Logical {
|
||||
using ConstraintTrait = std::true_type;
|
||||
Logical(Logical &&that) = default;
|
||||
Logical(A &&that) : thing(std::move(that)) {}
|
||||
Logical &operator=(Logical &&) = default;
|
||||
|
@ -300,6 +305,7 @@ template<typename A> struct Logical {
|
|||
};
|
||||
|
||||
template<typename A> struct DefaultChar {
|
||||
using ConstraintTrait = std::true_type;
|
||||
DefaultChar(DefaultChar &&that) = default;
|
||||
DefaultChar(A &&that) : thing(std::move(that)) {}
|
||||
DefaultChar &operator=(DefaultChar &&) = default;
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
namespace Fortran::parser {
|
||||
|
||||
const Name &GetLastName(const Name &x) { return x; }
|
||||
|
||||
const Name &GetLastName(const StructureComponent &x) {
|
||||
return GetLastName(x.component);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const DataRef &x) {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const Name &name) -> const Name & { return name; },
|
||||
[](const common::Indirection<StructureComponent> &sc)
|
||||
-> const Name & { return GetLastName(sc.value()); },
|
||||
[](const common::Indirection<ArrayElement> &sc) -> const Name & {
|
||||
return GetLastName(sc.value().base);
|
||||
},
|
||||
[](const common::Indirection<CoindexedNamedObject> &ci)
|
||||
-> const Name & { return GetLastName(ci.value().base); },
|
||||
},
|
||||
x.u);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Substring &x) {
|
||||
return GetLastName(std::get<DataRef>(x.t));
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Designator &x) {
|
||||
return std::visit(
|
||||
[](const auto &y) -> const Name & { return GetLastName(y); }, x.u);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const ProcComponentRef &x) {
|
||||
return GetLastName(x.v.thing);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const ProcedureDesignator &x) {
|
||||
return std::visit(
|
||||
[](const auto &y) -> const Name & { return GetLastName(y); }, x.u);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Call &x) {
|
||||
return GetLastName(std::get<ProcedureDesignator>(x.t));
|
||||
}
|
||||
|
||||
const Name &GetLastName(const FunctionReference &x) { return GetLastName(x.v); }
|
||||
|
||||
const Name &GetLastName(const Variable &x) {
|
||||
return std::visit(
|
||||
[](const auto &indirection) -> const Name & {
|
||||
return GetLastName(indirection.value());
|
||||
},
|
||||
x.u);
|
||||
}
|
||||
}
|
|
@ -14,62 +14,75 @@
|
|||
|
||||
#ifndef FORTRAN_PARSER_TOOLS_H_
|
||||
#define FORTRAN_PARSER_TOOLS_H_
|
||||
|
||||
#include "parse-tree.h"
|
||||
|
||||
namespace Fortran::parser {
|
||||
|
||||
// GetLastName() isolates and returns a reference to the rightmost Name
|
||||
// in a variable (i.e., the Name whose symbol's type determines the type
|
||||
// of the variable or expression).
|
||||
const Name &GetLastName(const Name &);
|
||||
const Name &GetLastName(const StructureComponent &);
|
||||
const Name &GetLastName(const DataRef &);
|
||||
const Name &GetLastName(const Substring &);
|
||||
const Name &GetLastName(const Designator &);
|
||||
const Name &GetLastName(const ProcComponentRef &);
|
||||
const Name &GetLastName(const ProcedureDesignator &);
|
||||
const Name &GetLastName(const Call &);
|
||||
const Name &GetLastName(const FunctionReference &);
|
||||
const Name &GetLastName(const Variable &);
|
||||
|
||||
const Name &GetLastName(const Name &x) { return x; }
|
||||
// When a parse tree node is an instance of a specific type wrapped in
|
||||
// layers of packaging, return a pointer to that object.
|
||||
// Implemented with mutually recursive template functions that are
|
||||
// wrapped in a struct to avoid prototypes.
|
||||
struct UnwrapperHelper {
|
||||
|
||||
const Name &GetLastName(const StructureComponent &x) {
|
||||
return GetLastName(x.component);
|
||||
}
|
||||
template<typename A, typename B> static const A *Unwrap(B *p) {
|
||||
if (p != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Name &GetLastName(const DataRef &x) {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const Name &name) { return GetLastName(name); },
|
||||
[](const common::Indirection<StructureComponent> &sc) {
|
||||
return GetLastName(sc.value());
|
||||
},
|
||||
[](const common::Indirection<ArrayElement> &sc) {
|
||||
return GetLastName(sc.value().base);
|
||||
},
|
||||
[](const common::Indirection<CoindexedNamedObject> &ci) {
|
||||
return GetLastName(ci.value().base);
|
||||
},
|
||||
},
|
||||
x.u);
|
||||
}
|
||||
template<typename A, typename B, bool COPY>
|
||||
static const A *Unwrap(const common::Indirection<B, COPY> &x) {
|
||||
return Unwrap<A>(x.value());
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Substring &x) {
|
||||
return GetType(std::get<DataRef>(x.t));
|
||||
}
|
||||
template<typename A, typename... Bs>
|
||||
static const A *Unwrap(const std::variant<Bs...> &x) {
|
||||
return std::visit([](const auto &y) { return Unwrap<A>(y); }, x);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Designator &x) {
|
||||
return std::visit([](const auto &y) { return GetType(y); }, x.u);
|
||||
}
|
||||
template<typename A, typename B>
|
||||
static const A *Unwrap(const std::optional<B> &o) {
|
||||
if (o.has_value()) {
|
||||
return Unwrap<A>(*o);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Name &GetLastName(const ProcComponentRef &x) {
|
||||
return GetType(x.v.thing);
|
||||
}
|
||||
template<typename A, typename B> static const A *Unwrap(B &x) {
|
||||
if constexpr (std::is_same_v<std::decay_t<A>, std::decay_t<B>>) {
|
||||
return &x;
|
||||
} else if constexpr (ConstraintTrait<B>) {
|
||||
return Unwrap<A>(x.thing);
|
||||
} else if constexpr (WrapperTrait<B>) {
|
||||
return Unwrap<A>(x.v);
|
||||
} else if constexpr (UnionTrait<B>) {
|
||||
return Unwrap<A>(x.u);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Name &GetLastName(const ProcedureDesignator &x) {
|
||||
return std::visit([](const auto &y) { return GetType(y); }, x.u);
|
||||
}
|
||||
|
||||
const Name &GetLastName(const Call &x) {
|
||||
return GetType(std::get<ProcedureDesignator>(x.t));
|
||||
}
|
||||
|
||||
const Name &GetLastName(const FunctionReference &x) { return GetType(x.v); }
|
||||
|
||||
const Name &GetLastName(const Variable &x) {
|
||||
return std::visit(
|
||||
[](const auto &indirection) { return GetType(indirection.value()); },
|
||||
x.u);
|
||||
template<typename A, typename B> const A *Unwrap(const B &x) {
|
||||
return UnwrapperHelper::Unwrap<A>(x);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "../evaluate/expression.h"
|
||||
#include "../parser/message.h"
|
||||
#include "../parser/parse-tree.h"
|
||||
#include "../parser/tools.h"
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
|
@ -56,7 +57,7 @@ void CoarrayChecker::Leave(const parser::FormTeamStmt &x) {
|
|||
AnalyzeExpr(context_, std::get<parser::ScalarIntExpr>(x.t));
|
||||
const auto &teamVar{std::get<parser::TeamVariable>(x.t)};
|
||||
AnalyzeExpr(context_, teamVar);
|
||||
const parser::Name *name{GetSimpleName(teamVar.thing)};
|
||||
const parser::Name *name{parser::Unwrap<parser::Name>(teamVar)};
|
||||
CHECK(name);
|
||||
if (const auto *type{name->symbol->GetType()}) {
|
||||
if (!IsTeamType(type->AsDerived())) {
|
||||
|
@ -86,8 +87,7 @@ void CoarrayChecker::CheckNamesAreDistinct(
|
|||
*prev, "Previous use of '%s'"_en_US);
|
||||
}
|
||||
// ResolveNames verified the selector is a simple name
|
||||
const auto &variable{std::get<parser::Variable>(selector.u)};
|
||||
const parser::Name *name{GetSimpleName(variable)};
|
||||
const parser::Name *name{parser::Unwrap<parser::Name>(selector)};
|
||||
CHECK(name);
|
||||
if (auto *prev{getPreviousUse(*name)}) {
|
||||
Say2(name->source, // C1113, C1115
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "../evaluate/type.h"
|
||||
#include "../parser/parse-tree-visitor.h"
|
||||
#include "../parser/parse-tree.h"
|
||||
#include "../parser/tools.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -4668,7 +4669,7 @@ bool ResolveNamesVisitor::Pre(const parser::PointerAssignmentStmt &x) {
|
|||
ResolveDataRef(dataRef);
|
||||
Walk(bounds);
|
||||
// Resolve unrestricted specific intrinsic procedures as in "p => cos".
|
||||
if (const parser::Name * name{GetSimpleName(expr)}) {
|
||||
if (const parser::Name * name{parser::Unwrap<parser::Name>(expr)}) {
|
||||
if (NameIsKnownOrIntrinsic(*name)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ bool RewriteMutator::Pre(parser::ExecutionPart &x) {
|
|||
|
||||
void RewriteMutator::Post(parser::IoUnit &x) {
|
||||
if (auto *var{std::get_if<parser::Variable>(&x.u)}) {
|
||||
parser::Name &last{parser::GetLastName(*var)};
|
||||
const parser::Name &last{parser::GetLastName(*var)};
|
||||
DeclTypeSpec *type{last.symbol ? last.symbol->GetType() : nullptr};
|
||||
if (type == nullptr || type->category() != DeclTypeSpec::Character) {
|
||||
// If the Variable is not known to be character (any kind), transform
|
||||
|
@ -134,13 +134,10 @@ void RewriteMutator::Post(parser::IoUnit &x) {
|
|||
// name had appeared with NML=.
|
||||
template<typename READ_OR_WRITE>
|
||||
void FixMisparsedUntaggedNamelistName(READ_OR_WRITE &x) {
|
||||
if (x.iounit.has_value() && x.format.has_value()) {
|
||||
if (auto *charExpr{
|
||||
std::get_if<parser::DefaultCharExpr>(&x.format.value().u)}) {
|
||||
parser::Expr &expr{charExpr->thing.value()};
|
||||
parser::Name *name{GetSimpleName(expr)};
|
||||
if (name != nullptr && name->symbol != nullptr &&
|
||||
name->symbol->has<NamelistDetails>()) {
|
||||
if (x.iounit.has_value() && x.format.has_value() &&
|
||||
std::holds_alternative<parser::DefaultCharExpr>(x.format->u)) {
|
||||
if (const parser::Name * name{parser::Unwrap<parser::Name>(x.format)}) {
|
||||
if (name->symbol != nullptr && name->symbol->has<NamelistDetails>()) {
|
||||
x.controls.emplace_front(parser::IoControlSpec{std::move(*name)});
|
||||
x.format.reset();
|
||||
}
|
||||
|
|
|
@ -97,11 +97,5 @@ const Symbol *FindExternallyVisibleObject(
|
|||
|
||||
bool ExprHasTypeCategory(
|
||||
const evaluate::GenericExprWrapper &expr, const common::TypeCategory &type);
|
||||
|
||||
// If this Expr or Variable represents a simple Name, return it.
|
||||
parser::Name *GetSimpleName(parser::Expr &);
|
||||
const parser::Name *GetSimpleName(const parser::Expr &);
|
||||
parser::Name *GetSimpleName(parser::Variable &);
|
||||
const parser::Name *GetSimpleName(const parser::Variable &);
|
||||
}
|
||||
#endif // FORTRAN_SEMANTICS_TOOLS_H_
|
||||
|
|
Loading…
Reference in New Issue