3ec5e102e2
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@3081f694e2 Reviewed-on: https://github.com/flang-compiler/f18/pull/338
163 lines
5.2 KiB
C++
163 lines
5.2 KiB
C++
// 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.
|
|
// 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 "semantics.h"
|
|
#include "assignment.h"
|
|
#include "canonicalize-do.h"
|
|
#include "check-do-concurrent.h"
|
|
#include "expression.h"
|
|
#include "mod-file.h"
|
|
#include "resolve-labels.h"
|
|
#include "resolve-names.h"
|
|
#include "rewrite-parse-tree.h"
|
|
#include "scope.h"
|
|
#include "symbol.h"
|
|
#include "../common/default-kinds.h"
|
|
#include "../parser/parse-tree-visitor.h"
|
|
#include <ostream>
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
static void DoDumpSymbols(std::ostream &, const Scope &, int indent = 0);
|
|
static void PutIndent(std::ostream &, int indent);
|
|
|
|
// A parse tree visitor that calls Enter/Leave functions from each checker
|
|
// class C supplied as template parameters. Enter is called before the node's
|
|
// children are visited, Leave is called after. No two checkers may have the
|
|
// same Enter or Leave function. Each checker must be constructible from
|
|
// SemanticsContext and have BaseChecker as a virtual base class.
|
|
template<typename... C> class SemanticsVisitor : public virtual C... {
|
|
public:
|
|
using C::Enter...;
|
|
using C::Leave...;
|
|
using BaseChecker::Enter;
|
|
using BaseChecker::Leave;
|
|
SemanticsVisitor(SemanticsContext &context)
|
|
: C{context}..., context_{context} {}
|
|
template<typename N> bool Pre(const N &node) {
|
|
Enter(node);
|
|
return true;
|
|
}
|
|
template<typename N> void Post(const N &node) { Leave(node); }
|
|
bool Walk(const parser::Program &program) {
|
|
parser::Walk(program, *this);
|
|
return !context_.AnyFatalError();
|
|
}
|
|
|
|
private:
|
|
SemanticsContext &context_;
|
|
};
|
|
|
|
using StatementSemanticsPass1 = SemanticsVisitor<ExprChecker>;
|
|
using StatementSemanticsPass2 =
|
|
SemanticsVisitor<AssignmentChecker, DoConcurrentChecker>;
|
|
|
|
SemanticsContext::SemanticsContext(const common::IntrinsicTypeDefaultKinds
|
|
&defaultKinds, const parser::LanguageFeatureControl &languageFeatures)
|
|
: defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures},
|
|
intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds)},
|
|
foldingContext_{evaluate::FoldingContext{
|
|
parser::ContextualMessages{parser::CharBlock{}, &messages_}}} {}
|
|
|
|
bool SemanticsContext::IsEnabled(parser::LanguageFeature feature) const {
|
|
return languageFeatures_.IsEnabled(feature);
|
|
}
|
|
|
|
bool SemanticsContext::ShouldWarn(parser::LanguageFeature feature) const {
|
|
return languageFeatures_.ShouldWarn(feature);
|
|
}
|
|
|
|
const DeclTypeSpec &SemanticsContext::MakeNumericType(
|
|
TypeCategory category, int kind) {
|
|
if (kind == 0) {
|
|
kind = defaultKinds_.GetDefaultKind(category);
|
|
}
|
|
return globalScope_.MakeNumericType(category, KindExpr{kind});
|
|
}
|
|
const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
|
|
if (kind == 0) {
|
|
kind = defaultKinds_.GetDefaultKind(TypeCategory::Logical);
|
|
}
|
|
return globalScope_.MakeLogicalType(KindExpr{kind});
|
|
}
|
|
|
|
bool SemanticsContext::AnyFatalError() const {
|
|
return !messages_.empty() &&
|
|
(warningsAreErrors_ || messages_.AnyFatalError());
|
|
}
|
|
|
|
const Scope &SemanticsContext::FindScope(
|
|
const parser::CharBlock &source) const {
|
|
if (const auto *scope{globalScope_.FindScope(source)}) {
|
|
return *scope;
|
|
} else {
|
|
common::die("invalid source location");
|
|
}
|
|
}
|
|
|
|
bool Semantics::Perform() {
|
|
return ValidateLabels(context_.messages(), program_) &&
|
|
parser::CanonicalizeDo(program_) && // force line break
|
|
ResolveNames(context_, program_) &&
|
|
RewriteParseTree(context_, program_) &&
|
|
StatementSemanticsPass1{context_}.Walk(program_) &&
|
|
StatementSemanticsPass2{context_}.Walk(program_) &&
|
|
ModFileWriter{context_}.WriteAll();
|
|
}
|
|
|
|
void Semantics::EmitMessages(std::ostream &os) const {
|
|
context_.messages().Emit(os, cooked_);
|
|
}
|
|
|
|
void Semantics::DumpSymbols(std::ostream &os) {
|
|
DoDumpSymbols(os, context_.globalScope());
|
|
}
|
|
|
|
void DoDumpSymbols(std::ostream &os, const Scope &scope, int indent) {
|
|
PutIndent(os, indent);
|
|
os << Scope::EnumToString(scope.kind()) << " scope:";
|
|
if (const auto *symbol{scope.symbol()}) {
|
|
os << ' ' << symbol->name().ToString();
|
|
}
|
|
os << '\n';
|
|
++indent;
|
|
for (const auto &pair : scope) {
|
|
const auto &symbol{*pair.second};
|
|
PutIndent(os, indent);
|
|
os << symbol << '\n';
|
|
if (const auto *details{symbol.detailsIf<GenericDetails>()}) {
|
|
if (const auto &type{details->derivedType()}) {
|
|
PutIndent(os, indent);
|
|
os << *type << '\n';
|
|
}
|
|
}
|
|
}
|
|
for (const auto &pair : scope.commonBlocks()) {
|
|
const auto &symbol{*pair.second};
|
|
PutIndent(os, indent);
|
|
os << symbol << '\n';
|
|
}
|
|
for (const auto &child : scope.children()) {
|
|
DoDumpSymbols(os, child, indent);
|
|
}
|
|
--indent;
|
|
}
|
|
|
|
static void PutIndent(std::ostream &os, int indent) {
|
|
for (int i = 0; i < indent; ++i) {
|
|
os << " ";
|
|
}
|
|
}
|
|
|
|
}
|