[flang] Avoid segfault in association semantics by cleaning up code a bit

Original-commit: flang-compiler/f18@04d40c3d75
Reviewed-on: https://github.com/flang-compiler/f18/pull/488
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-06-06 14:34:54 -07:00
parent 8a326cb7f7
commit b408a02c14
5 changed files with 103 additions and 63 deletions

View File

@ -1422,7 +1422,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
bool ok{false};
if (const auto &arg{specificCall->arguments[0]}) {
if (const auto *expr{arg->UnwrapExpr()}) {
if (const Symbol * symbol{IsWholeSymbolDataRef(*expr)}) {
if (const Symbol * symbol{UnwrapWholeSymbolDataRef(*expr)}) {
ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
}
}

View File

@ -72,9 +72,16 @@ template<typename A> bool IsVariable(const FunctionRef<A> &funcRef) {
return false;
}
}
template<typename A> bool IsVariable(const Expr<A> &expr) {
template<typename T> bool IsVariable(const Expr<T> &expr) {
return std::visit([](const auto &x) { return IsVariable(x); }, expr.u);
}
template<typename A> bool IsVariable(const std::optional<A> &x) {
if (x.has_value()) {
return IsVariable(*x);
} else {
return false;
}
}
// Predicate: true when an expression is assumed-rank
bool IsAssumedRank(const semantics::Symbol &);
@ -88,9 +95,16 @@ template<typename A> bool IsAssumedRank(const Designator<A> &designator) {
return false;
}
}
template<typename A> bool IsAssumedRank(const Expr<A> &expr) {
template<typename T> bool IsAssumedRank(const Expr<T> &expr) {
return std::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
}
template<typename A> bool IsAssumedRank(const std::optional<A> &x) {
if (x.has_value()) {
return IsAssumedRank(*x);
} else {
return false;
}
}
// Generalizing packagers: these take operations and expressions of more
// specific types and wrap them in Expr<> containers of more abstract types.
@ -145,7 +159,7 @@ template<typename A> constexpr bool IsNumericCategoryExpr() {
// Specializing extractor. If an Expr wraps some type of object, perhaps
// in several layers, return a pointer to it; otherwise null. Also works
// with ActualArgument.
// with expressions contained in ActualArgument.
template<typename A, typename B>
auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
using Ty = std::decay_t<B>;
@ -188,7 +202,6 @@ template<typename A>
common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(const A &) {
return std::nullopt; // default base casec
}
template<typename T>
std::optional<DataRef> ExtractDataRef(const Designator<T> &d) {
return std::visit(
@ -200,12 +213,10 @@ std::optional<DataRef> ExtractDataRef(const Designator<T> &d) {
},
d.u);
}
template<typename T>
std::optional<DataRef> ExtractDataRef(const Expr<T> &expr) {
return std::visit([](const auto &x) { return ExtractDataRef(x); }, expr.u);
}
template<typename A>
std::optional<DataRef> ExtractDataRef(const std::optional<A> &x) {
if (x.has_value()) {
@ -217,7 +228,7 @@ std::optional<DataRef> ExtractDataRef(const std::optional<A> &x) {
// If an expression is simply a whole symbol data designator,
// extract and return that symbol, else null.
template<typename A> const Symbol *IsWholeSymbolDataRef(const A &x) {
template<typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
if (auto dataRef{ExtractDataRef(x)}) {
if (const Symbol **p{std::get_if<const Symbol *>(&dataRef->u)}) {
return *p;
@ -603,27 +614,36 @@ struct TypeKindVisitor {
VALUE value;
};
// GetLastSymbol() returns the rightmost symbol in an object or procedure
// designator (possibly wrapped in an Expr<>), or a null pointer if
// none is found.
template<typename A> const semantics::Symbol *GetLastSymbol(const A &) {
return nullptr;
}
template<typename T>
const semantics::Symbol *GetLastSymbol(const Designator<T> &x) {
return x.GetLastSymbol();
}
inline const semantics::Symbol *GetLastSymbol(const ProcedureDesignator &x) {
return x.GetSymbol();
}
inline const semantics::Symbol *GetLastSymbol(const ProcedureRef &x) {
return GetLastSymbol(x.proc());
}
template<typename T> const semantics::Symbol *GetLastSymbol(const Expr<T> &x) {
return std::visit([](const auto &y) { return GetLastSymbol(y); }, x.u);
}
template<typename A>
const semantics::Symbol *GetLastSymbol(const std::optional<A> &x) {
if (x.has_value()) {
return GetLastSymbol(*x);
} else {
return nullptr;
}
}
// Convenience: If GetLastSymbol() succeeds on the argument, return its
// set of attributes, otherwise the empty set.
template<typename A> semantics::Attrs GetAttrs(const A &x) {
if (const semantics::Symbol * symbol{GetLastSymbol(x)}) {
return symbol->attrs();
@ -632,11 +652,13 @@ template<typename A> semantics::Attrs GetAttrs(const A &x) {
}
}
// Predicate: IsAllocatableOrPointer()
template<typename A> bool IsAllocatableOrPointer(const A &x) {
return GetAttrs(x).HasAny(
semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
}
// Predicate: IsProcedurePointer()
template<typename A> bool IsProcedurePointer(const A &) { return false; }
inline bool IsProcedurePointer(const ProcedureDesignator &) { return true; }
inline bool IsProcedurePointer(const ProcedureRef &) { return true; }
@ -644,5 +666,12 @@ inline bool IsProcedurePointer(const Expr<SomeType> &expr) {
return std::visit(
[](const auto &x) { return IsProcedurePointer(x); }, expr.u);
}
template<typename A> bool IsProcedurePointer(const std::optional<A> &x) {
if (x.has_value()) {
return IsProcedurePointer(*x);
} else {
return false;
}
}
}
#endif // FORTRAN_EVALUATE_TOOLS_H_

View File

@ -207,6 +207,19 @@ Statement<ActionStmt> StmtFunctionStmt::ConvertToAssignment() {
AssignmentStmt{std::move(variable), std::move(funcExpr)}}}};
}
CharBlock Variable::GetSource() const {
return std::visit(
common::visitors{
[&](const common::Indirection<Designator> &des) {
return des.value().source;
},
[&](const common::Indirection<parser::FunctionReference> &call) {
return call.value().v.source;
},
},
u);
}
std::ostream &operator<<(std::ostream &os, const Name &x) {
return os << x.ToString();
}

View File

@ -1773,6 +1773,7 @@ struct Designator {
struct Variable {
UNION_CLASS_BOILERPLATE(Variable);
mutable Expr::TypedExpr typedExpr;
parser::CharBlock GetSource() const;
std::variant<common::Indirection<Designator>,
common::Indirection<FunctionReference>>
u;

View File

@ -928,13 +928,11 @@ private:
// expr is set in either case unless there were errors
struct Selector {
Selector() {}
Selector(const parser::CharBlock &source, MaybeExpr &&expr,
const parser::Name *variable = nullptr)
: source{source}, expr{std::move(expr)}, variable{variable} {}
Selector(const parser::CharBlock &source, MaybeExpr &&expr)
: source{source}, expr{std::move(expr)} {}
operator bool() const { return expr.has_value(); }
parser::CharBlock source;
MaybeExpr expr;
const parser::Name *variable{nullptr};
};
// association -> [associate-name =>] selector
struct {
@ -4017,7 +4015,8 @@ void ConstructVisitor::Post(const parser::CoarrayAssociation &x) {
if (auto *symbol{FindInScope(currScope(), name)}) {
const auto &selector{std::get<parser::Selector>(x.t)};
if (auto sel{ResolveSelector(selector)}) {
if (!sel.variable || sel.variable->symbol->Corank() == 0) {
const Symbol *whole{UnwrapWholeSymbolDataRef(sel.expr)};
if (!whole || whole->Corank() == 0) {
Say(sel.source, // C1116
"Selector in coarray association must name a coarray"_err_en_US);
} else if (auto dynType{sel.expr->GetType()}) {
@ -4040,8 +4039,8 @@ void ConstructVisitor::Post(const parser::SelectTypeStmt &x) {
MakePlaceholder(*name, MiscDetails::Kind::SelectTypeAssociateName);
association_.name = &*name;
} else {
const auto *varName{association_.selector.variable};
if (!varName || !varName->symbol->has<ObjectEntityDetails>()) {
const Symbol *whole{UnwrapWholeSymbolDataRef(association_.selector.expr)};
if (!whole || !whole->has<ObjectEntityDetails>()) {
Say(association_.selector.source, // C1157
"Selector is not a named variable: 'associate-name =>' is required"_err_en_US);
association_ = {};
@ -4094,55 +4093,55 @@ Symbol *ConstructVisitor::MakeAssocEntity() {
return nullptr;
}
if (auto &expr{association_.selector.expr}) {
symbol.set_details(AssocEntityDetails{std::move(*expr)});
association_.selector.expr.reset();
symbol.set_details(AssocEntityDetails{common::Clone(*expr)});
} else {
symbol.set_details(AssocEntityDetails{});
}
return &symbol;
}
// Set the type of symbol based on the current association variable or expr.
// Set the type of symbol based on the current association selector.
void ConstructVisitor::SetTypeFromAssociation(Symbol &symbol) {
if (association_.selector.variable) {
const Symbol *varSymbol{association_.selector.variable->symbol};
CHECK(varSymbol);
if (const DeclTypeSpec * type{varSymbol->GetType()}) {
symbol.SetType(*type);
}
} else {
auto &details{symbol.get<AssocEntityDetails>()};
if (const MaybeExpr & expr{details.expr()}) {
if (std::optional<evaluate::DynamicType> type{expr->GetType()}) {
if (const auto *charExpr{
evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeCharacter>>(
*expr)}) {
symbol.SetType(ToDeclTypeSpec(std::move(*type),
FoldExpr(std::visit(
[](const auto &kindChar) { return kindChar.LEN(); },
charExpr->u))));
} else {
symbol.SetType(ToDeclTypeSpec(std::move(*type)));
auto &details{symbol.get<AssocEntityDetails>()};
const MaybeExpr *pexpr{&details.expr()};
if (!pexpr->has_value()) {
pexpr = &association_.selector.expr;
}
if (pexpr->has_value()) {
const SomeExpr &expr{**pexpr};
if (evaluate::IsVariable(expr)) {
if (const Symbol * varSymbol{evaluate::GetLastSymbol(expr)}) {
if (const DeclTypeSpec * type{varSymbol->GetType()}) {
symbol.SetType(*type);
return;
}
} else {
// BOZ literal not acceptable
Say(symbol.name(), "Associate name '%s' must have a type"_err_en_US);
}
}
if (std::optional<evaluate::DynamicType> type{expr.GetType()}) {
if (const auto *charExpr{
evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeCharacter>>(
expr)}) {
symbol.SetType(ToDeclTypeSpec(std::move(*type),
FoldExpr(
std::visit([](const auto &kindChar) { return kindChar.LEN(); },
charExpr->u))));
} else {
symbol.SetType(ToDeclTypeSpec(std::move(*type)));
}
} else {
// BOZ literals, procedure designators, &c. are not acceptable
Say(symbol.name(), "Associate name '%s' must have a type"_err_en_US);
}
}
}
// If current selector is a variable, set some of its attributes on symbol.
void ConstructVisitor::SetAttrsFromAssociation(Symbol &symbol) {
if (association_.selector.variable) {
if (const auto *varSymbol{association_.selector.variable->symbol}) {
symbol.attrs() |= varSymbol->attrs() &
Attrs{Attr::TARGET, Attr::ASYNCHRONOUS, Attr::VOLATILE,
Attr::CONTIGUOUS};
if (varSymbol->attrs().test(Attr::POINTER)) {
symbol.attrs().set(Attr::TARGET);
}
}
Attrs attrs{evaluate::GetAttrs(association_.selector.expr)};
symbol.attrs() |= attrs &
Attrs{Attr::TARGET, Attr::ASYNCHRONOUS, Attr::VOLATILE, Attr::CONTIGUOUS};
if (attrs.test(Attr::POINTER)) {
symbol.attrs().set(Attr::TARGET);
}
}
@ -4150,15 +4149,11 @@ ConstructVisitor::Selector ConstructVisitor::ResolveSelector(
const parser::Selector &x) {
return std::visit(
common::visitors{
[&](const parser::Expr &y) {
return Selector{y.source, EvaluateExpr(y)};
[&](const parser::Expr &expr) {
return Selector{expr.source, EvaluateExpr(expr)};
},
[&](const parser::Variable &y) {
if (const auto *variable{ResolveVariable(y)}) {
return Selector{variable->source, EvaluateExpr(y), variable};
} else {
return Selector{};
}
[&](const parser::Variable &var) {
return Selector{var.GetSource(), EvaluateExpr(var)};
},
},
x.u);
@ -4174,8 +4169,10 @@ const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec(
case common::TypeCategory::Logical:
return context().MakeLogicalType(type.kind());
case common::TypeCategory::Derived:
return currScope().MakeDerivedType(
DeclTypeSpec::TypeDerived, DerivedTypeSpec{type.GetDerivedTypeSpec()});
return currScope().MakeDerivedType(type.isPolymorphic()
? DeclTypeSpec::ClassDerived
: DeclTypeSpec::TypeDerived,
DerivedTypeSpec{type.GetDerivedTypeSpec()});
case common::TypeCategory::Character:
default: CRASH_NO_CASE;
}