llvm/flang/lib/semantics/resolve-names-utils.cc

290 lines
9.9 KiB
C++
Raw Normal View History

[flang] Name resolution for defined operators Instead of tracking just genericName_ while in a generic interface block or generic statement, now we immediately create a symbol for it. A parser::Name isn't good enough because a defined-operator or defined-io-generic-spec doesn't have a name. Change the parse tree to add a source field to GenericSpec. Use these as names for symbols for defined-operator and defined-io-generic-spec (e.g. "operator(+)" or "read(formatted)"). Change the source for defined-op-name to include the dots so that they can be distinguished from normal symbols with the same name (e.g. you can have both ".foo." and "foo"). These symbols have names in the symbol table like ".foo.", not "operator(.foo.)", because references to them have that form. Add GenericKind enum to GenericDetails and GenericBindingDetails. This allows us to know a symbol is "assignment(=)", for example, without having to do a string comparison. Add GenericSpecInfo to handle analyzing the various kinds of generic-spec and generating symbol names and GenericKind for them. Add reference to LanguageFeatureControl to SemanticsContext so that they can be checked during semantics. For this change, if LogicalAbbreviations is enabled, report an error if the user tries to define an operator named ".T." or ".F.". Add resolve-name-utils.cc to hold utility functions and classes that don't have to be in the ResolveNamesVisitor class hierarchy. The goal is to reduce the size of resolve-names.cc where possible. Original-commit: flang-compiler/f18@3081f694e21dbcaef2554198a682c9af57f2e185 Reviewed-on: https://github.com/flang-compiler/f18/pull/338
2019-03-18 19:48:02 +01:00
// 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 "resolve-names-utils.h"
#include "expression.h"
[flang] Name resolution for defined operators Instead of tracking just genericName_ while in a generic interface block or generic statement, now we immediately create a symbol for it. A parser::Name isn't good enough because a defined-operator or defined-io-generic-spec doesn't have a name. Change the parse tree to add a source field to GenericSpec. Use these as names for symbols for defined-operator and defined-io-generic-spec (e.g. "operator(+)" or "read(formatted)"). Change the source for defined-op-name to include the dots so that they can be distinguished from normal symbols with the same name (e.g. you can have both ".foo." and "foo"). These symbols have names in the symbol table like ".foo.", not "operator(.foo.)", because references to them have that form. Add GenericKind enum to GenericDetails and GenericBindingDetails. This allows us to know a symbol is "assignment(=)", for example, without having to do a string comparison. Add GenericSpecInfo to handle analyzing the various kinds of generic-spec and generating symbol names and GenericKind for them. Add reference to LanguageFeatureControl to SemanticsContext so that they can be checked during semantics. For this change, if LogicalAbbreviations is enabled, report an error if the user tries to define an operator named ".T." or ".F.". Add resolve-name-utils.cc to hold utility functions and classes that don't have to be in the ResolveNamesVisitor class hierarchy. The goal is to reduce the size of resolve-names.cc where possible. Original-commit: flang-compiler/f18@3081f694e21dbcaef2554198a682c9af57f2e185 Reviewed-on: https://github.com/flang-compiler/f18/pull/338
2019-03-18 19:48:02 +01:00
#include "semantics.h"
#include "../common/idioms.h"
#include "../evaluate/fold.h"
#include "../evaluate/tools.h"
#include "../evaluate/type.h"
[flang] Name resolution for defined operators Instead of tracking just genericName_ while in a generic interface block or generic statement, now we immediately create a symbol for it. A parser::Name isn't good enough because a defined-operator or defined-io-generic-spec doesn't have a name. Change the parse tree to add a source field to GenericSpec. Use these as names for symbols for defined-operator and defined-io-generic-spec (e.g. "operator(+)" or "read(formatted)"). Change the source for defined-op-name to include the dots so that they can be distinguished from normal symbols with the same name (e.g. you can have both ".foo." and "foo"). These symbols have names in the symbol table like ".foo.", not "operator(.foo.)", because references to them have that form. Add GenericKind enum to GenericDetails and GenericBindingDetails. This allows us to know a symbol is "assignment(=)", for example, without having to do a string comparison. Add GenericSpecInfo to handle analyzing the various kinds of generic-spec and generating symbol names and GenericKind for them. Add reference to LanguageFeatureControl to SemanticsContext so that they can be checked during semantics. For this change, if LogicalAbbreviations is enabled, report an error if the user tries to define an operator named ".T." or ".F.". Add resolve-name-utils.cc to hold utility functions and classes that don't have to be in the ResolveNamesVisitor class hierarchy. The goal is to reduce the size of resolve-names.cc where possible. Original-commit: flang-compiler/f18@3081f694e21dbcaef2554198a682c9af57f2e185 Reviewed-on: https://github.com/flang-compiler/f18/pull/338
2019-03-18 19:48:02 +01:00
#include "../parser/char-block.h"
#include "../parser/features.h"
#include "../parser/parse-tree.h"
#include <ostream>
#include <variant>
namespace Fortran::semantics {
using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
static GenericKind MapIntrinsicOperator(IntrinsicOperator);
Symbol *Resolve(const parser::Name &name, Symbol *symbol) {
if (symbol && !name.symbol) {
name.symbol = symbol;
}
return symbol;
}
Symbol &Resolve(const parser::Name &name, Symbol &symbol) {
return *Resolve(name, &symbol);
}
parser::MessageFixedText WithIsFatal(
const parser::MessageFixedText &msg, bool isFatal) {
return parser::MessageFixedText{
msg.text().begin(), msg.text().size(), isFatal};
}
bool IsDefinedOperator(const SourceName &name) {
const char *begin{name.begin()};
const char *end{name.end()};
return begin != end && begin[0] == '.' && end[-1] == '.';
}
bool IsInstrinsicOperator(
const SemanticsContext &context, const SourceName &name) {
std::string str{name.ToString()};
std::set<std::string> intrinsics{".and.", ".eq.", ".eqv.", ".ge.", ".gt.",
".le.", ".lt.", ".ne.", ".neqv.", ".not.", ".or."};
if (intrinsics.count(str) > 0) {
return true;
}
if (context.IsEnabled(parser::LanguageFeature::XOROperator) &&
str == ".xor.") {
return true;
}
if (context.IsEnabled(parser::LanguageFeature::LogicalAbbreviations) &&
(str == ".n." || str == ".a" || str == ".o." || str == ".x.")) {
return true;
}
return false;
}
bool IsLogicalConstant(
const SemanticsContext &context, const SourceName &name) {
std::string str{name.ToString()};
return str == ".true." || str == ".false." ||
(context.IsEnabled(parser::LanguageFeature::LogicalAbbreviations) &&
(str == ".t" || str == ".f."));
}
void GenericSpecInfo::Resolve(Symbol *symbol) {
if (symbol) {
if (auto *details{symbol->detailsIf<GenericDetails>()}) {
details->set_kind(kind_);
} else if (auto *details{symbol->detailsIf<GenericBindingDetails>()}) {
details->set_kind(kind_);
}
if (parseName_) {
semantics::Resolve(*parseName_, symbol);
}
}
}
void GenericSpecInfo::Analyze(const parser::DefinedOpName &name) {
kind_ = GenericKind::DefinedOp;
parseName_ = &name.v;
symbolName_ = &name.v.source;
}
void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
symbolName_ = &x.source;
kind_ = std::visit(
common::visitors{
[&](const parser::Name &y) {
parseName_ = &y;
symbolName_ = &y.source;
return GenericKind::Name;
},
[&](const parser::DefinedOperator &y) {
return std::visit(
common::visitors{
[&](const parser::DefinedOpName &z) {
Analyze(z);
return GenericKind::DefinedOp;
},
[&](const IntrinsicOperator &z) {
return MapIntrinsicOperator(z);
},
},
y.u);
},
[&](const parser::GenericSpec::Assignment &y) {
return GenericKind::Assignment;
},
[&](const parser::GenericSpec::ReadFormatted &y) {
return GenericKind::ReadFormatted;
},
[&](const parser::GenericSpec::ReadUnformatted &y) {
return GenericKind::ReadUnformatted;
},
[&](const parser::GenericSpec::WriteFormatted &y) {
return GenericKind::WriteFormatted;
},
[&](const parser::GenericSpec::WriteUnformatted &y) {
return GenericKind::WriteUnformatted;
},
},
x.u);
}
// parser::DefinedOperator::IntrinsicOperator -> GenericKind
static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
switch (op) {
case IntrinsicOperator::Power: return GenericKind::OpPower;
case IntrinsicOperator::Multiply: return GenericKind::OpMultiply;
case IntrinsicOperator::Divide: return GenericKind::OpDivide;
case IntrinsicOperator::Add: return GenericKind::OpAdd;
case IntrinsicOperator::Subtract: return GenericKind::OpSubtract;
case IntrinsicOperator::Concat: return GenericKind::OpConcat;
case IntrinsicOperator::LT: return GenericKind::OpLT;
case IntrinsicOperator::LE: return GenericKind::OpLE;
case IntrinsicOperator::EQ: return GenericKind::OpEQ;
case IntrinsicOperator::NE: return GenericKind::OpNE;
case IntrinsicOperator::GE: return GenericKind::OpGE;
case IntrinsicOperator::GT: return GenericKind::OpGT;
case IntrinsicOperator::NOT: return GenericKind::OpNOT;
case IntrinsicOperator::AND: return GenericKind::OpAND;
case IntrinsicOperator::OR: return GenericKind::OpOR;
case IntrinsicOperator::XOR: return GenericKind::OpXOR;
case IntrinsicOperator::EQV: return GenericKind::OpEQV;
case IntrinsicOperator::NEQV: return GenericKind::OpNEQV;
default: CRASH_NO_CASE;
}
}
class ArraySpecAnalyzer {
public:
ArraySpecAnalyzer(SemanticsContext &context) : context_{context} {}
ArraySpec Analyze(const parser::ArraySpec &);
ArraySpec Analyze(const parser::ComponentArraySpec &);
ArraySpec Analyze(const parser::CoarraySpec &);
private:
SemanticsContext &context_;
ArraySpec arraySpec_;
template<typename T> void Analyze(const std::list<T> &list) {
for (const auto &elem : list) {
Analyze(elem);
}
}
void Analyze(const parser::AssumedShapeSpec &);
void Analyze(const parser::ExplicitShapeSpec &);
void Analyze(const parser::AssumedImpliedSpec &);
void Analyze(const parser::DeferredShapeSpecList &);
void Analyze(const parser::AssumedRankSpec &);
void MakeExplicit(const std::optional<parser::SpecificationExpr> &,
const parser::SpecificationExpr &);
void MakeImplied(const std::optional<parser::SpecificationExpr> &);
void MakeDeferred(int);
Bound GetBound(const std::optional<parser::SpecificationExpr> &);
Bound GetBound(const parser::SpecificationExpr &);
};
ArraySpec AnalyzeArraySpec(
SemanticsContext &context, const parser::ArraySpec &arraySpec) {
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
}
ArraySpec AnalyzeArraySpec(
SemanticsContext &context, const parser::ComponentArraySpec &arraySpec) {
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
}
ArraySpec AnalyzeCoarraySpec(
SemanticsContext &context, const parser::CoarraySpec &coarraySpec) {
return ArraySpecAnalyzer{context}.Analyze(coarraySpec);
}
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
std::visit([this](const auto &y) { Analyze(y); }, x.u);
return arraySpec_;
}
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
std::visit(
common::visitors{
[&](const parser::AssumedSizeSpec &y) {
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
},
[&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
[&](const auto &y) { Analyze(y); },
},
x.u);
return arraySpec_;
}
ArraySpec ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
std::visit(
common::visitors{
[&](const parser::DeferredCoshapeSpecList &y) { MakeDeferred(y.v); },
[&](const parser::ExplicitCoshapeSpec &y) {
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
MakeImplied(
std::get<std::optional<parser::SpecificationExpr>>(y.t));
},
},
x.u);
return arraySpec_;
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
arraySpec_.push_back(ShapeSpec::MakeAssumed(GetBound(x.v)));
}
void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
std::get<parser::SpecificationExpr>(x.t));
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
MakeImplied(x.v);
}
void ArraySpecAnalyzer::Analyze(const parser::DeferredShapeSpecList &x) {
MakeDeferred(x.v);
}
void ArraySpecAnalyzer::Analyze(const parser::AssumedRankSpec &) {
arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
}
void ArraySpecAnalyzer::MakeExplicit(
const std::optional<parser::SpecificationExpr> &lb,
const parser::SpecificationExpr &ub) {
arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub)));
}
void ArraySpecAnalyzer::MakeImplied(
const std::optional<parser::SpecificationExpr> &lb) {
arraySpec_.push_back(ShapeSpec::MakeImplied(GetBound(lb)));
}
void ArraySpecAnalyzer::MakeDeferred(int n) {
for (int i = 0; i < n; ++i) {
arraySpec_.push_back(ShapeSpec::MakeDeferred());
}
}
Bound ArraySpecAnalyzer::GetBound(
const std::optional<parser::SpecificationExpr> &x) {
return x ? GetBound(*x) : Bound{1};
}
Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
MaybeSubscriptIntExpr expr;
if (MaybeExpr maybeExpr{AnalyzeExpr(context_, x.v)}) {
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
expr = evaluate::Fold(context_.foldingContext(),
evaluate::ConvertToType<evaluate::SubscriptInteger>(
std::move(*intExpr)));
}
}
return Bound{std::move(expr)};
}
[flang] Name resolution for defined operators Instead of tracking just genericName_ while in a generic interface block or generic statement, now we immediately create a symbol for it. A parser::Name isn't good enough because a defined-operator or defined-io-generic-spec doesn't have a name. Change the parse tree to add a source field to GenericSpec. Use these as names for symbols for defined-operator and defined-io-generic-spec (e.g. "operator(+)" or "read(formatted)"). Change the source for defined-op-name to include the dots so that they can be distinguished from normal symbols with the same name (e.g. you can have both ".foo." and "foo"). These symbols have names in the symbol table like ".foo.", not "operator(.foo.)", because references to them have that form. Add GenericKind enum to GenericDetails and GenericBindingDetails. This allows us to know a symbol is "assignment(=)", for example, without having to do a string comparison. Add GenericSpecInfo to handle analyzing the various kinds of generic-spec and generating symbol names and GenericKind for them. Add reference to LanguageFeatureControl to SemanticsContext so that they can be checked during semantics. For this change, if LogicalAbbreviations is enabled, report an error if the user tries to define an operator named ".T." or ".F.". Add resolve-name-utils.cc to hold utility functions and classes that don't have to be in the ResolveNamesVisitor class hierarchy. The goal is to reduce the size of resolve-names.cc where possible. Original-commit: flang-compiler/f18@3081f694e21dbcaef2554198a682c9af57f2e185 Reviewed-on: https://github.com/flang-compiler/f18/pull/338
2019-03-18 19:48:02 +01:00
}