diff --git a/flang/include/flang/Common/indirection.h b/flang/include/flang/Common/indirection.h --- a/flang/include/flang/Common/indirection.h +++ b/flang/include/flang/Common/indirection.h @@ -122,20 +122,49 @@ template using CopyableIndirection = Indirection; -// 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> works. -template class Deleter { +// A variation of std::unique_ptr<> with a reified deletion routine. +// Used to avoid dependence cycles between shared libraries. +template class ForwardOwningPointer { public: - void operator()(A *) const; + ForwardOwningPointer() {} + ForwardOwningPointer(A *p, void (*del)(A *)) : p_{p}, deleter_{del} {} + ForwardOwningPointer(ForwardOwningPointer &&that) + : p_{that.p_}, deleter_{that.deleter_} { + that.p_ = nullptr; + } + ForwardOwningPointer &operator=(ForwardOwningPointer &&that) { + p_ = that.p_; + that.p_ = nullptr; + deleter_ = that.deleter_; + return *this; + } + ~ForwardOwningPointer() { + if (p_) { + deleter_(p_); + } + } + + A &operator*() const { return *p_; } + A *operator->() const { return p_; } + operator bool() const { return p_ != nullptr; } + A *get() { return p_; } + A *release() { + A *result{p_}; + p_ = nullptr; + return result; + } + + void Reset(A *p, void (*del)(A *)) { + if (p_) { + deleter_(p_); + } + p_ = p; + deleter_ = del; + } + +private: + A *p_{nullptr}; + void (*deleter_)(A *){nullptr}; }; } // namespace Fortran::common -#define DEFINE_DELETER(A) \ - template <> void Fortran::common::Deleter::operator()(A *p) const { \ - delete p; \ - } #endif // FORTRAN_COMMON_INDIRECTION_H_ diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h --- a/flang/include/flang/Evaluate/call.h +++ b/flang/include/flang/Evaluate/call.h @@ -195,6 +195,7 @@ : proc_{std::move(p)}, arguments_{std::move(a)}, hasAlternateReturns_{hasAlternateReturns} {} ~ProcedureRef(); + static void Deleter(ProcedureRef *); ProcedureDesignator &proc() { return proc_; } const ProcedureDesignator &proc() const { return proc_; } diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h --- a/flang/include/flang/Evaluate/expression.h +++ b/flang/include/flang/Evaluate/expression.h @@ -841,6 +841,7 @@ explicit GenericExprWrapper(std::optional> &&x) : v{std::move(x)} {} ~GenericExprWrapper(); + static void Deleter(GenericExprWrapper *); std::optional> v; // vacant if error }; @@ -849,6 +850,7 @@ GenericAssignmentWrapper() {} explicit GenericAssignmentWrapper(Assignment &&x) : v{std::move(x)} {} ~GenericAssignmentWrapper(); + static void Deleter(GenericAssignmentWrapper *); std::optional v; // vacant if error }; diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -908,6 +908,7 @@ bool IsProcedurePointer(const Symbol &); bool IsSaved(const Symbol &); // saved implicitly or explicitly bool IsDummy(const Symbol &); +bool IsFunctionResult(const Symbol &); // Follow use, host, and construct assocations to a variable, if any. const Symbol *GetAssociationRoot(const Symbol &); diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -1395,8 +1395,7 @@ using ConstantSubobject = Constant>; // Represents an analyzed expression -using TypedExpr = std::unique_ptr>; +using TypedExpr = common::ForwardOwningPointer; // R845 data-stmt-constant -> // scalar-constant | scalar-constant-subobject | @@ -1919,8 +1918,8 @@ // R1032 assignment-stmt -> variable = expr struct AssignmentStmt { TUPLE_CLASS_BOILERPLATE(AssignmentStmt); - using TypedAssignment = std::unique_ptr>; + using TypedAssignment = + common::ForwardOwningPointer; mutable TypedAssignment typedAssignment; std::tuple t; }; @@ -3140,8 +3139,7 @@ // R1521 call-stmt -> CALL procedure-designator [( [actual-arg-spec-list] )] struct CallStmt { WRAPPER_CLASS_BOILERPLATE(CallStmt, Call); - mutable std::unique_ptr> + mutable common::ForwardOwningPointer typedCall; // filled by semantics }; diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h --- a/flang/include/flang/Semantics/expression.h +++ b/flang/include/flang/Semantics/expression.h @@ -70,7 +70,8 @@ struct SetExprHelper { explicit SetExprHelper(GenericExprWrapper &&expr) : expr_{std::move(expr)} {} void Set(parser::TypedExpr &x) { - x.reset(new GenericExprWrapper{std::move(expr_)}); + x.Reset(new GenericExprWrapper{std::move(expr_)}, + evaluate::GenericExprWrapper::Deleter); } void Set(const parser::Expr &x) { Set(x.typedExpr); } void Set(const parser::Variable &x) { Set(x.typedExpr); } diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h --- a/flang/include/flang/Semantics/tools.h +++ b/flang/include/flang/Semantics/tools.h @@ -85,7 +85,6 @@ bool IsBindCProcedure(const Symbol &); bool IsBindCProcedure(const Scope &); bool IsProcName(const Symbol &); // proc-name -bool IsFunctionResult(const Symbol &); bool IsFunctionResultWithSameNameAsFunction(const Symbol &); bool IsExtensibleType(const DerivedTypeSpec *); bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name); diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt --- a/flang/lib/Evaluate/CMakeLists.txt +++ b/flang/lib/Evaluate/CMakeLists.txt @@ -41,7 +41,6 @@ LINK_LIBS FortranCommon FortranDecimal - FortranSemantics FortranParser ${LIBPGMATH} diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp --- a/flang/lib/Evaluate/call.cpp +++ b/flang/lib/Evaluate/call.cpp @@ -212,6 +212,7 @@ ProcedureRef::~ProcedureRef() {} +void ProcedureRef::Deleter(ProcedureRef *p) { delete p; } + FOR_EACH_SPECIFIC_TYPE(template class FunctionRef, ) } // namespace Fortran::evaluate -DEFINE_DELETER(Fortran::evaluate::ProcedureRef) diff --git a/flang/lib/Evaluate/expression.cpp b/flang/lib/Evaluate/expression.cpp --- a/flang/lib/Evaluate/expression.cpp +++ b/flang/lib/Evaluate/expression.cpp @@ -223,8 +223,14 @@ GenericExprWrapper::~GenericExprWrapper() {} +void GenericExprWrapper::Deleter(GenericExprWrapper *p) { delete p; } + GenericAssignmentWrapper::~GenericAssignmentWrapper() {} +void GenericAssignmentWrapper::Deleter(GenericAssignmentWrapper *p) { + delete p; +} + template int Expr>::GetKind() const { return std::visit( [](const auto &kx) { return std::decay_t::Result::kind; }, @@ -243,5 +249,3 @@ INSTANTIATE_EXPRESSION_TEMPLATES } // namespace Fortran::evaluate -DEFINE_DELETER(Fortran::evaluate::GenericExprWrapper) -DEFINE_DELETER(Fortran::evaluate::GenericAssignmentWrapper) diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -1026,6 +1026,13 @@ symbol.details()); } +bool IsFunctionResult(const Symbol &symbol) { + return (symbol.has() && + symbol.get().isFuncResult()) || + (symbol.has() && + symbol.get().isFuncResult()); +} + int CountLenParameters(const DerivedTypeSpec &type) { return std::count_if(type.parameters().begin(), type.parameters().end(), [](const auto &pair) { return pair.second.isLen(); }); diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt --- a/flang/lib/Lower/CMakeLists.txt +++ b/flang/lib/Lower/CMakeLists.txt @@ -22,6 +22,10 @@ LINK_LIBS FIROptimizer ${dialect_libs} + FortranCommon + FortranParser + FortranEvaluate + FortranSemantics MLIRAffineToStandard MLIRLLVMIR MLIRSCFToStandard diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp --- a/flang/lib/Parser/parse-tree.cpp +++ b/flang/lib/Parser/parse-tree.cpp @@ -14,16 +14,6 @@ #include "llvm/Support/raw_ostream.h" #include -// So "delete Expr;" calls an external destructor for its typedExpr. -namespace Fortran::evaluate { -struct GenericExprWrapper { - ~GenericExprWrapper(); -}; -struct GenericAssignmentWrapper { - ~GenericAssignmentWrapper(); -}; -} // namespace Fortran::evaluate - namespace Fortran::parser { // R867 @@ -258,6 +248,3 @@ } } // namespace Fortran::parser - -template class std::unique_ptr; -template class std::unique_ptr; diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -44,6 +44,7 @@ LINK_LIBS FortranCommon + FortranParser FortranEvaluate LINK_COMPONENTS diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -2031,8 +2031,10 @@ if (CheckCall(call.source, *proc, callee->arguments)) { bool hasAlternateReturns{ callee->arguments.size() < actualArgList.size()}; - callStmt.typedCall.reset(new ProcedureRef{std::move(*proc), - std::move(callee->arguments), hasAlternateReturns}); + callStmt.typedCall.Reset( + new ProcedureRef{std::move(*proc), std::move(callee->arguments), + hasAlternateReturns}, + ProcedureRef::Deleter); } } } @@ -2044,7 +2046,8 @@ analyzer.Analyze(std::get(x.t)); analyzer.Analyze(std::get(x.t)); if (analyzer.fatalErrors()) { - x.typedAssignment.reset(new GenericAssignmentWrapper{}); + x.typedAssignment.Reset( + new GenericAssignmentWrapper{}, GenericAssignmentWrapper::Deleter); } else { std::optional procRef{analyzer.TryDefinedAssignment()}; Assignment assignment{ @@ -2052,8 +2055,9 @@ if (procRef) { assignment.u = std::move(*procRef); } - x.typedAssignment.reset( - new GenericAssignmentWrapper{std::move(assignment)}); + x.typedAssignment.Reset( + new GenericAssignmentWrapper{std::move(assignment)}, + GenericAssignmentWrapper::Deleter); } } return common::GetPtrFromOptional(x.typedAssignment->v); @@ -2065,7 +2069,8 @@ MaybeExpr lhs{Analyze(std::get(x.t))}; MaybeExpr rhs{Analyze(std::get(x.t))}; if (!lhs || !rhs) { - x.typedAssignment.reset(new GenericAssignmentWrapper{}); + x.typedAssignment.Reset( + new GenericAssignmentWrapper{}, GenericAssignmentWrapper::Deleter); } else { Assignment assignment{std::move(*lhs), std::move(*rhs)}; std::visit(common::visitors{ @@ -2092,8 +2097,9 @@ }, }, std::get(x.t).u); - x.typedAssignment.reset( - new GenericAssignmentWrapper{std::move(assignment)}); + x.typedAssignment.Reset( + new GenericAssignmentWrapper{std::move(assignment)}, + GenericAssignmentWrapper::Deleter); } } return common::GetPtrFromOptional(x.typedAssignment->v); @@ -2934,7 +2940,7 @@ const parser::Expr &expr) { source_.ExtendToCover(expr.source); if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) { - expr.typedExpr.reset(new GenericExprWrapper{}); + expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter); if (allowAssumedType_) { return ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}}; } else { diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp --- a/flang/lib/Semantics/tools.cpp +++ b/flang/lib/Semantics/tools.cpp @@ -1217,13 +1217,6 @@ return nullptr; } -bool IsFunctionResult(const Symbol &symbol) { - return (symbol.has() && - symbol.get().isFuncResult()) || - (symbol.has() && - symbol.get().isFuncResult()); -} - bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) { if (IsFunctionResult(symbol)) { if (const Symbol * function{symbol.owner().symbol()}) { diff --git a/flang/tools/f18-parse-demo/stub-evaluate.cpp b/flang/tools/f18-parse-demo/stub-evaluate.cpp --- a/flang/tools/f18-parse-demo/stub-evaluate.cpp +++ b/flang/tools/f18-parse-demo/stub-evaluate.cpp @@ -9,25 +9,19 @@ // The parse tree has slots in which pointers to the results of semantic // analysis may be placed. When using the parser without the semantics // libraries, as here, we need to stub out the dependences on the external -// destructors, which will never actually be called. - -#include "flang/Common/indirection.h" +// deleters, which will never actually be called. namespace Fortran::evaluate { struct GenericExprWrapper { - ~GenericExprWrapper(); + static void Deleter(GenericExprWrapper *); }; -GenericExprWrapper::~GenericExprWrapper() {} +void GenericExprWrapper::Deleter(GenericExprWrapper *) {} struct GenericAssignmentWrapper { - ~GenericAssignmentWrapper(); + static void Deleter(GenericAssignmentWrapper *); }; -GenericAssignmentWrapper::~GenericAssignmentWrapper() {} +void GenericAssignmentWrapper::Deleter(GenericAssignmentWrapper *) {} struct ProcedureRef { - ~ProcedureRef(); + static void Deleter(ProcedureRef *); }; -ProcedureRef::~ProcedureRef() {} +void ProcedureRef::Deleter(ProcedureRef *) {} } // namespace Fortran::evaluate - -DEFINE_DELETER(Fortran::evaluate::GenericExprWrapper) -DEFINE_DELETER(Fortran::evaluate::GenericAssignmentWrapper) -DEFINE_DELETER(Fortran::evaluate::ProcedureRef) diff --git a/flang/tools/f18/CMakeLists.txt b/flang/tools/f18/CMakeLists.txt --- a/flang/tools/f18/CMakeLists.txt +++ b/flang/tools/f18/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + FrontendOpenMP Support ) add_flang_tool(f18 diff --git a/flang/unittests/Evaluate/CMakeLists.txt b/flang/unittests/Evaluate/CMakeLists.txt --- a/flang/unittests/Evaluate/CMakeLists.txt +++ b/flang/unittests/Evaluate/CMakeLists.txt @@ -4,6 +4,10 @@ fp-testing.cpp ) +target_link_libraries(FortranEvaluateTesting + LLVMSupport +) + add_executable(leading-zero-bit-count-test leading-zero-bit-count.cpp ) diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -8,6 +8,11 @@ ) llvm_update_compile_flags(RuntimeTesting) +target_link_libraries(RuntimeTesting + FortranRuntime + LLVMSupport +) + add_executable(format-test format.cpp )