From af2940cfb66a48abef594513ed25b5029f4b3992 Mon Sep 17 00:00:00 2001 From: Tim Keith Date: Wed, 11 Apr 2018 13:11:42 -0700 Subject: [PATCH] [flang] Continue work on name resolution. In Symbol and Scope, use SourceName (== parser::CharBlock) in place of Name (== std::string) so that names in the symtab have provenance. We may ultimately have multiple source locations associated with a symbol. ImplicitRules: Add isImplicitNoneType and isImplicitNoneExternal flags. MessageHandler: Add methods to emit messages associated with source locations. Detect conflicting declarations of symbols (e.g. as subprogram and variable). Handle attribute statements like ASYNCHRONOUS statement: make sure the symbol is present and set the attribute on it. Check that these all have a type declared by the end of the specification part if IMPLICIT NONE. Find variables named in Variable and Expr nodes and check they have types declared if IMPLICIT NONE. Otherwise, make sure they are in the symtab and apply the implicit type rules at the end of the scope. Push a scope for MainProgram nodes and add a symbol for the name if it has one. Rename Scope::map_type to Scope::mapType Add tests for errors currently being detected. There is no framework for running them yet, just source files with the expected errors in comments. Original-commit: flang-compiler/f18@39e6fa4169644b2c2b94ee611949fbc0766599a2 Reviewed-on: https://github.com/flang-compiler/f18/pull/49 Tree-same-pre-rewrite: false --- flang/lib/semantics/resolve-names.cc | 343 ++++++++++++++++++++++++++++------- flang/lib/semantics/scope.h | 25 +-- flang/lib/semantics/symbol.cc | 6 +- flang/lib/semantics/symbol.h | 37 ++-- flang/test/semantics/implicit01.f90 | 11 ++ flang/test/semantics/implicit02.f90 | 11 ++ flang/test/semantics/implicit03.f90 | 11 ++ flang/test/semantics/implicit04.f90 | 5 + flang/test/semantics/implicit05.f90 | 4 + flang/test/semantics/implicit06.f90 | 12 ++ flang/test/semantics/resolve01.f90 | 4 + flang/test/semantics/resolve02.f90 | 7 + flang/test/semantics/resolve03.f90 | 5 + flang/test/semantics/resolve04.f90 | 10 + flang/test/semantics/resolve05.f90 | 14 ++ flang/test/semantics/resolve06.f90 | 6 + 16 files changed, 417 insertions(+), 94 deletions(-) create mode 100644 flang/test/semantics/implicit01.f90 create mode 100644 flang/test/semantics/implicit02.f90 create mode 100644 flang/test/semantics/implicit03.f90 create mode 100644 flang/test/semantics/implicit04.f90 create mode 100644 flang/test/semantics/implicit05.f90 create mode 100644 flang/test/semantics/implicit06.f90 create mode 100644 flang/test/semantics/resolve01.f90 create mode 100644 flang/test/semantics/resolve02.f90 create mode 100644 flang/test/semantics/resolve03.f90 create mode 100644 flang/test/semantics/resolve04.f90 create mode 100644 flang/test/semantics/resolve05.f90 create mode 100644 flang/test/semantics/resolve06.f90 diff --git a/flang/lib/semantics/resolve-names.cc b/flang/lib/semantics/resolve-names.cc index dba7b5c..c9f4f96 100644 --- a/flang/lib/semantics/resolve-names.cc +++ b/flang/lib/semantics/resolve-names.cc @@ -21,18 +21,24 @@ class MessageHandler; class ImplicitRules { public: ImplicitRules(MessageHandler &messages); + bool isImplicitNoneType() const { return isImplicitNoneType_; } + bool isImplicitNoneExternal() const { return isImplicitNoneExternal_; } + void set_isImplicitNoneType(bool x) { isImplicitNoneType_ = x; } + void set_isImplicitNoneExternal(bool x) { isImplicitNoneExternal_ = x; } // Get the implicit type for identifiers starting with ch. May be null. const DeclTypeSpec *GetType(char ch) const; // Record the implicit type for this range of characters. void SetType(const DeclTypeSpec &type, parser::Location lo, parser::Location, bool isDefault = false); // Apply the default implicit rules (if no IMPLICIT NONE). - void ApplyDefaultRules(); + void AddDefaultRules(); private: static char Incr(char ch); MessageHandler &messages_; + bool isImplicitNoneType_{false}; + bool isImplicitNoneExternal_{false}; // map initial character of identifier to nullptr or its default type std::map map_; friend std::ostream &operator<<(std::ostream &, const ImplicitRules &); @@ -138,6 +144,9 @@ public: void Say(Message &&); void Say(parser::MessageFixedText &&); void Say(parser::MessageFormattedText &&); + // Emit a message about a name or source location + void Say(const parser::Name &, parser::MessageFixedText &&); + void Say(const parser::CharBlock &, parser::MessageFixedText &&); private: // Where messages are emitted: @@ -162,7 +171,12 @@ public: bool Pre(const parser::LetterSpec &); bool Pre(const parser::ImplicitSpec &); void Post(const parser::ImplicitSpec &); - void Post(const parser::ImplicitPart &); + + ImplicitRules &implicitRules() { return implicitRules_.top(); } + const ImplicitRules &implicitRules() const { return implicitRules_.top(); } + bool isImplicitNoneType() const { + return implicitRules().isImplicitNoneType(); + } protected: void PushScope(); @@ -209,6 +223,17 @@ public: void Post(const parser::TypeDeclarationStmt &); void Post(const parser::EntityDecl &); bool Pre(const parser::PrefixSpec &); + bool Pre(const parser::AsynchronousStmt &); + bool Pre(const parser::ContiguousStmt &); + bool Pre(const parser::ExternalStmt &); + bool Pre(const parser::IntrinsicStmt &); + bool Pre(const parser::OptionalStmt &); + bool Pre(const parser::ProtectedStmt &); + bool Pre(const parser::ValueStmt &); + bool Pre(const parser::VolatileStmt &); + bool Pre(const parser::AllocatableStmt &); + bool Pre(const parser::TargetStmt &); + void Post(const parser::SpecificationPart &); void Post(const parser::EndSubroutineStmt &); void Post(const parser::EndFunctionStmt &); bool Pre(const parser::Suffix &); @@ -216,29 +241,104 @@ public: void Post(const parser::SubroutineStmt &); bool Pre(const parser::FunctionStmt &); void Post(const parser::FunctionStmt &); + bool Pre(const parser::MainProgram &); + void Post(const parser::EndProgramStmt &); void Post(const parser::Program &); + const parser::Name *GetVariableName(const parser::DataReference &x) { + return std::get_if(&x.u); + } + const parser::Name *GetVariableName(const parser::Designator &x) { + return std::visit( + parser::visitors{ + [&](const parser::ObjectName &x) { return &x; }, + [&](const parser::DataReference &x) { return GetVariableName(x); }, + [&](const auto &) { + return static_cast(nullptr); + }, + }, + x.u); + } + const parser::Name *GetVariableName(const parser::Expr &x) { + if (const auto *designator = + std::get_if>(&x.u)) { + return GetVariableName(**designator); + } else { + return nullptr; + } + } + const parser::Name *GetVariableName(const parser::Variable &x) { + if (const auto *designator = + std::get_if>(&x.u)) { + return GetVariableName(**designator); + } else { + return nullptr; + } + } + + void Post(const parser::Expr &x) { + CheckImplicitSymbol(GetVariableName(x)); + } + void Post(const parser::Variable &x) { + CheckImplicitSymbol(GetVariableName(x)); + } + + // If implicit types are allowed, ensure name is in the symbol table + void CheckImplicitSymbol(const parser::Name *name) { + if (name) { + if (!isImplicitNoneType()) { + // ensure this name is in symbol table: + CurrScope().try_emplace(name->source); + } else { + const auto &it = CurrScope().find(name->source); + if (it == CurrScope().end() || it->second.has()) { + Say(*name, "No explicit type declared for '%s'"_err_en_US); + } + } + } + } + private: // Stack of containing scopes; memory referenced is owned by parent scopes std::stack> scopes_; - std::optional funcResultName_; + // Function result name from parser::Suffix, if any. + const parser::Name *funcResultName_{nullptr}; + + // Create a subprogram symbol in the current scope and push a new scope. + void PushSubprogramScope(const parser::Name &, SubprogramDetails &); + + // On leaving a scope, add implicit types if appropriate. + void ApplyImplicitRules(); - // Common Post() for functions and subroutines. - // Create a symbol in the current scope, push a new scope, add the dummies. - void PostSubprogram(const Name &name, const std::list &dummyNames); + // Handle a statement that sets an attribute on a list of names. + bool HandleAttributeStmt(Attr, const std::list &); + bool HandleAttributeStmt(Attr, const std::list &); // Helpers to make a Symbol in the current scope template - std::pair MakeSymbol( - const Name &name, const Attrs &attrs, D &&details) { - return CurrScope().try_emplace(name, attrs, details); + Symbol &MakeSymbol( + const parser::Name &name, const Attrs &attrs, D &&details) { + const auto pair = CurrScope().try_emplace(name.source, attrs, details); + Symbol &symbol = pair.first->second; + if (!pair.second) { + symbol.attrs() |= attrs; + if (!std::is_same::value) { + if (symbol.has()) { + symbol.set_details(details); + } else { + Say(name, "'%s' is already declared in this scoping unit"_err_en_US); + Say(symbol.name(), "Previous declaration of '%s'"_en_US); + } + } + } + return symbol; } template - std::pair MakeSymbol(const Name &name, D &&details) { + Symbol &MakeSymbol(const parser::Name &name, D &&details) { return MakeSymbol(name, Attrs(), details); } - std::pair MakeSymbol(const Name &name) { - return CurrScope().try_emplace(name, UnknownDetails()); + Symbol &MakeSymbol(const parser::Name &name, Attrs attrs) { + return MakeSymbol(name, attrs, UnknownDetails()); } }; @@ -268,7 +368,7 @@ void ImplicitRules::SetType(const DeclTypeSpec &type, parser::Location lo, } } -void ImplicitRules::ApplyDefaultRules() { +void ImplicitRules::AddDefaultRules() { SetType(DeclTypeSpec::MakeIntrinsic(IntegerTypeSpec::Make()), "i", "n", true); SetType(DeclTypeSpec::MakeIntrinsic(RealTypeSpec::Make()), "a", "z", true); } @@ -457,6 +557,16 @@ void MessageHandler::Say(parser::MessageFormattedText &&x) { messages_.Put(Message{currStmtSource_->begin(), std::move(x)}); } +void MessageHandler::Say( + const parser::CharBlock &source, parser::MessageFixedText &&msg) { + Say(parser::Message{source.begin(), + parser::MessageFormattedText{msg, source.ToString().c_str()}}); +} +void MessageHandler::Say( + const parser::Name &name, parser::MessageFixedText &&msg) { + Say(name.source, std::move(msg)); +} + // ImplicitRulesVisitor implementation void ImplicitRulesVisitor::Post(const parser::ParameterStmt &x) { @@ -496,16 +606,10 @@ bool ImplicitRulesVisitor::Pre(const parser::LetterSpec &x) { return false; } } - implicitRules_.top().SetType(*declTypeSpec_.get(), loLoc, hiLoc); + implicitRules().SetType(*declTypeSpec_.get(), loLoc, hiLoc); return false; } -void ImplicitRulesVisitor::Post(const parser::ImplicitPart &) { - if (!prevImplicitNoneType_) { - implicitRules_.top().ApplyDefaultRules(); - } -} - bool ImplicitRulesVisitor::Pre(const parser::ImplicitSpec &) { beginDeclTypeSpec(); return true; @@ -539,6 +643,7 @@ bool ImplicitRulesVisitor::HandleImplicitNone( prevImplicitNone_ = currStmtSource(); if (nameSpecs.empty()) { prevImplicitNoneType_ = currStmtSource(); + implicitRules().set_isImplicitNoneType(true); if (prevImplicit_) { Say("IMPLICIT NONE statement after IMPLICIT statement"_err_en_US); return false; @@ -549,6 +654,7 @@ bool ImplicitRulesVisitor::HandleImplicitNone( for (const auto noneSpec : nameSpecs) { switch (noneSpec) { case ImplicitNoneNameSpec::External: + implicitRules().set_isImplicitNoneExternal(true); ++sawExternal; // TODO: // C894 If IMPLICIT NONE with an implicit-none-spec of EXTERNAL @@ -559,6 +665,7 @@ bool ImplicitRulesVisitor::HandleImplicitNone( break; case ImplicitNoneNameSpec::Type: prevImplicitNoneType_ = currStmtSource(); + implicitRules().set_isImplicitNoneType(true); if (prevImplicit_) { Say("IMPLICIT NONE(TYPE) after IMPLICIT statement"_err_en_US); return false; @@ -587,24 +694,19 @@ void ResolveNamesVisitor::Post(const parser::EntityDecl &x) { const auto &name{std::get(x.t)}; // TODO: optional ArraySpec, CoarraySpec, CharLength, Initialization - Symbol &symbol{MakeSymbol(name.ToString()).first->second}; - symbol.attrs() |= *attrs_; // TODO: check attribute consistency + Symbol &symbol{MakeSymbol(name, *attrs_)}; // TODO: check attribute consistency if (symbol.has()) { symbol.set_details(EntityDetails()); } if (EntityDetails *details = symbol.detailsIf()) { if (details->type().has_value()) { - Say(parser::Message{name.source.begin(), - parser::MessageFormattedText{ - "'%s' already has a type declared"_err_en_US, - name.ToString().c_str()}}); + Say(name, "'%s' already has a type declared"_err_en_US); } else { details->set_type(*declTypeSpec_); } } else { - Say(parser::Message{name.source.begin(), - parser::MessageFormattedText{ - "'%s' is already declared"_err_en_US, name.ToString().c_str()}}); + Say(name, "'%s' is already declared in this scoping unit"_err_en_US); + Say(symbol.name(), "Previous declaration of '%s'"_en_US); } } @@ -619,26 +721,122 @@ void ResolveNamesVisitor::Post(const parser::TypeDeclarationStmt &x) { endAttrs(); } -bool ResolveNamesVisitor::Pre(const parser::PrefixSpec &stmt) { +bool ResolveNamesVisitor::Pre(const parser::PrefixSpec &x) { return true; // TODO } +bool ResolveNamesVisitor::Pre(const parser::AsynchronousStmt &x) { + return HandleAttributeStmt(Attr::ASYNCHRONOUS, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::ContiguousStmt &x) { + return HandleAttributeStmt(Attr::CONTIGUOUS, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::ExternalStmt &x) { + return HandleAttributeStmt(Attr::EXTERNAL, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::IntrinsicStmt &x) { + return HandleAttributeStmt(Attr::INTRINSIC, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::OptionalStmt &x) { + return HandleAttributeStmt(Attr::OPTIONAL, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::ProtectedStmt &x) { + return HandleAttributeStmt(Attr::PROTECTED, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::ValueStmt &x) { + return HandleAttributeStmt(Attr::VALUE, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::VolatileStmt &x) { + return HandleAttributeStmt(Attr::VOLATILE, x.v); +} +bool ResolveNamesVisitor::HandleAttributeStmt( + Attr attr, const std::list &names) { + for (const auto &name : names) { + const auto pair = CurrScope().try_emplace(name.source, Attrs{attr}); + if (!pair.second) { + // symbol was already there: set attribute on it + pair.first->second.attrs().set(attr); + } + } + return false; +} + +bool ResolveNamesVisitor::Pre(const parser::AllocatableStmt &x) { + return HandleAttributeStmt(Attr::ALLOCATABLE, x.v); +} +bool ResolveNamesVisitor::Pre(const parser::TargetStmt &x) { + return HandleAttributeStmt(Attr::TARGET, x.v); +} +bool ResolveNamesVisitor::HandleAttributeStmt( + Attr attr, const std::list &decls) { + for (const auto &decl : decls) { + const auto &name = std::get(decl.t); + //TODO: std::get>(decl.t) + //TODO: std::get>(decl.t) + const auto pair = CurrScope().try_emplace(name.source, Attrs{attr}); + if (!pair.second) { + // symbol was already there: set attribute on it + pair.first->second.attrs().set(attr); + } + } + return false; +} + +void ResolveNamesVisitor::Post(const parser::SpecificationPart &s) { + if (isImplicitNoneType()) { + // Check that every name referenced has an explicit type + for (const auto &pair : CurrScope()) { + const auto &name = pair.first; + const auto &symbol = pair.second; + if (symbol.has()) { + Say(name, "No explicit type declared for '%s'"_err_en_US); + } else if (const auto *details = symbol.detailsIf()) { + if (!details->type()) { + Say(name, "No explicit type declared for '%s'"_err_en_US); + } + } + } + } +} + void ResolveNamesVisitor::Post(const parser::EndSubroutineStmt &subp) { + ApplyImplicitRules(); std::cout << "End of subroutine scope\n"; std::cout << CurrScope(); PopScope(); } void ResolveNamesVisitor::Post(const parser::EndFunctionStmt &subp) { + ApplyImplicitRules(); std::cout << "End of function scope\n"; std::cout << CurrScope(); PopScope(); } -bool ResolveNamesVisitor::Pre(const parser::Suffix &suffix) { - if (suffix.resultName.has_value()) { - funcResultName_ = std::make_optional(suffix.resultName->ToString()); +void ResolveNamesVisitor::ApplyImplicitRules() { + if (!isImplicitNoneType()) { + implicitRules().AddDefaultRules(); + for (auto &pair : CurrScope()) { + Symbol &symbol = pair.second; + if (symbol.has()) { + symbol.set_details(EntityDetails()); + } + if (auto *details = symbol.detailsIf()) { + if (!details->type()) { + const auto &name = pair.first; + if (const auto *type = implicitRules().GetType(name.begin()[0])) { + details->set_type(*type); + } else { + Say(name, "No explicit type declared for '%s'"_err_en_US); + } + } + } + } } +} + +bool ResolveNamesVisitor::Pre(const parser::Suffix &suffix) { + funcResultName_ = &suffix.resultName.value(); return true; } @@ -646,20 +844,6 @@ bool ResolveNamesVisitor::Pre(const parser::SubroutineStmt &stmt) { beginAttrs(); return true; } - -void ResolveNamesVisitor::Post(const parser::SubroutineStmt &stmt) { - Name subrName = std::get(stmt.t).ToString(); - std::list dummyNames; - const auto &dummyArgs = std::get>(stmt.t); - for (const parser::DummyArg &dummyArg : dummyArgs) { - const parser::Name *dummyName = std::get_if(&dummyArg.u); - CHECK(dummyName != nullptr && "TODO: alternate return indicator"); - dummyNames.push_back(dummyName->ToString()); - } - PostSubprogram(subrName, dummyNames); - MakeSymbol(subrName, SubprogramDetails(dummyNames)); -} - bool ResolveNamesVisitor::Pre(const parser::FunctionStmt &stmt) { beginAttrs(); beginDeclTypeSpec(); @@ -667,37 +851,66 @@ bool ResolveNamesVisitor::Pre(const parser::FunctionStmt &stmt) { return true; } +void ResolveNamesVisitor::Post(const parser::SubroutineStmt &stmt) { + const auto &subrName = std::get(stmt.t); + SubprogramDetails details; + PushSubprogramScope(subrName, details); + for (const auto &dummyArg : std::get>(stmt.t)) { + const parser::Name *dummyName = std::get_if(&dummyArg.u); + CHECK(dummyName != nullptr && "TODO: alternate return indicator"); + MakeSymbol(*dummyName, EntityDetails(true)); + details.AddDummyName(dummyName->source); + } +} + void ResolveNamesVisitor::Post(const parser::FunctionStmt &stmt) { - Name funcName = std::get(stmt.t).ToString(); - std::list dummyNames; - for (const auto &dummy : std::get>(stmt.t)) { - dummyNames.push_back(dummy.ToString()); + const auto &funcName = std::get(stmt.t); + const auto &funcResultName = funcResultName_ ? *funcResultName_ : funcName; + funcResultName_ = nullptr; + SubprogramDetails details(funcResultName.source); + PushSubprogramScope(funcName, details); + for (const auto &dummyName : std::get>(stmt.t)) { + MakeSymbol(dummyName, EntityDetails(true)); + details.AddDummyName(dummyName.source); } - PostSubprogram(funcName, dummyNames); // add function result to function scope EntityDetails funcResultDetails; if (declTypeSpec_) { funcResultDetails.set_type(*declTypeSpec_); } - const auto &resultName = funcResultName_ ? *funcResultName_ : funcName; - MakeSymbol(resultName, funcResultDetails); - if (resultName != funcName) { - // add symbol for function to its scope; name can't be reused - MakeSymbol(funcName, SubprogramDetails(dummyNames, funcResultName_)); - } endDeclTypeSpec(); - funcResultName_ = std::nullopt; + if (funcResultName.source != funcName.source) { + MakeSymbol(funcResultName, funcResultDetails); + } else { + CurrScope().erase(funcName.source); // was added by PushSubprogramScope + MakeSymbol(funcName, funcResultDetails); + } } -void ResolveNamesVisitor::PostSubprogram( - const Name &name, const std::list &dummyNames) { - const auto attrs = endAttrs(); - MakeSymbol(name, attrs, SubprogramDetails(dummyNames)); +void ResolveNamesVisitor::PushSubprogramScope( + const parser::Name &name, SubprogramDetails &details) { + MakeSymbol(name, endAttrs(), details); Scope &subpScope = CurrScope().MakeScope(Scope::Kind::Subprogram); PushScope(subpScope); - for (const auto &dummyName : dummyNames) { - MakeSymbol(dummyName, EntityDetails(true)); + MakeSymbol(name, details); // can't reused this name inside subprogram +} + +bool ResolveNamesVisitor::Pre(const parser::MainProgram &x) { + Scope &scope = CurrScope().MakeScope(Scope::Kind::MainProgram); + PushScope(scope); + using stmtType = std::optional>; + if (const stmtType &stmt = std::get(x.t)) { + const parser::Name &name{stmt->statement.v}; + MakeSymbol(name, MainProgramDetails()); } + return true; +} + +void ResolveNamesVisitor::Post(const parser::EndProgramStmt &) { + ApplyImplicitRules(); + std::cout << "End of program scope\n"; + std::cout << CurrScope(); + PopScope(); } void ResolveNamesVisitor::Post(const parser::Program &) { diff --git a/flang/lib/semantics/scope.h b/flang/lib/semantics/scope.h index 448888d..6fcd035 100644 --- a/flang/lib/semantics/scope.h +++ b/flang/lib/semantics/scope.h @@ -12,7 +12,7 @@ namespace Fortran::semantics { class Scope { - using map_type = std::map; + using mapType = std::map; public: // root of the scope tree; contains intrinsics: @@ -38,9 +38,9 @@ public: /// Make a scope nested in this one Scope &MakeScope(Kind kind); - using size_type = map_type::size_type; - using iterator = map_type::iterator; - using const_iterator = map_type::const_iterator; + using size_type = mapType::size_type; + using iterator = mapType::iterator; + using const_iterator = mapType::const_iterator; iterator begin() { return symbols_.begin(); } iterator end() { return symbols_.end(); } @@ -49,24 +49,27 @@ public: const_iterator cbegin() const { return symbols_.cbegin(); } const_iterator cend() const { return symbols_.cend(); } - iterator find(const Name &name) { return symbols_.find(name); } - const_iterator find(const Name &name) const { return symbols_.find(name); } - size_type erase(const Name &name) { return symbols_.erase(name); } + iterator find(const SourceName &name) { return symbols_.find(name); } + const_iterator find(const SourceName &name) const { + return symbols_.find(name); + } + + size_type erase(const SourceName &name) { return symbols_.erase(name); } /// Make a Symbol with unknown details. std::pair try_emplace( - const Name &name, Attrs attrs = Attrs()) { + const SourceName &name, Attrs attrs = Attrs()) { return try_emplace(name, attrs, UnknownDetails()); } /// Make a Symbol with provided details. template - std::pair try_emplace(const Name &name, D &&details) { + std::pair try_emplace(const SourceName &name, D &&details) { return try_emplace(name, Attrs(), details); } /// Make a Symbol with attrs and details template std::pair try_emplace( - const Name &name, Attrs attrs, D &&details) { + const SourceName &name, Attrs attrs, D &&details) { return symbols_.try_emplace(name, *this, name, attrs, details); } @@ -74,7 +77,7 @@ private: const Scope &parent_; const Kind kind_; std::list children_; - map_type symbols_; + mapType symbols_; friend std::ostream &operator<<(std::ostream &, const Scope &); }; diff --git a/flang/lib/semantics/symbol.cc b/flang/lib/semantics/symbol.cc index b735289..bd25699 100644 --- a/flang/lib/semantics/symbol.cc +++ b/flang/lib/semantics/symbol.cc @@ -6,7 +6,7 @@ namespace Fortran::semantics { std::ostream &operator<<(std::ostream &os, const Symbol &sym) { - os << sym.name(); + os << sym.name_.ToString(); if (!sym.attrs().empty()) { os << ", " << sym.attrs(); } @@ -21,11 +21,11 @@ std::ostream &operator<<(std::ostream &os, const Symbol &sym) { int n = 0; for (const auto &dummy : x.dummyNames()) { if (n++ > 0) os << ", "; - os << dummy; + os << dummy.ToString(); } os << ')'; if (x.resultName()) { - os << " result(" << *x.resultName() << ')'; + os << " result(" << x.resultName()->ToString() << ')'; } }, [&](const EntityDetails &x) { diff --git a/flang/lib/semantics/symbol.h b/flang/lib/semantics/symbol.h index 3742527..87573d6 100644 --- a/flang/lib/semantics/symbol.h +++ b/flang/lib/semantics/symbol.h @@ -7,6 +7,10 @@ namespace Fortran::semantics { +/// A SourceName is a name in the cooked character stream, +/// i.e. a range of characters with provenance. +using SourceName = parser::CharBlock; + /// A Symbol consists of common information (name, owner, and attributes) /// and details information specific to the kind of symbol, represented by the /// *Details classes. @@ -25,20 +29,19 @@ private: class SubprogramDetails { public: - SubprogramDetails(const std::list &dummyNames) - : isFunction_{false}, dummyNames_{dummyNames} {} - SubprogramDetails( - const std::list &dummyNames, const std::optional &resultName) - : isFunction_{true}, dummyNames_{dummyNames}, resultName_{resultName} {} + // Subroutine: + SubprogramDetails() {} + // Function: + SubprogramDetails(const SourceName &resultName) : resultName_{resultName} {} - bool isFunction() const { return isFunction_; } - const std::list &dummyNames() const { return dummyNames_; } - const std::optional &resultName() const { return resultName_; } + bool isFunction() const { return resultName_.has_value(); } + const std::list &dummyNames() const { return dummyNames_; } + const std::optional &resultName() const { return resultName_; } + void AddDummyName(const SourceName &name) { dummyNames_.push_back(name); } private: - bool isFunction_; - std::list dummyNames_; - std::optional resultName_; + std::list dummyNames_; + std::optional resultName_; friend std::ostream &operator<<(std::ostream &, const SubprogramDetails &); }; @@ -63,11 +66,12 @@ public: using Details = std::variant; - Symbol(const Scope &owner, const Name &name, const Attrs &attrs, + Symbol(const Scope &owner, const SourceName &name, const Attrs &attrs, Details &&details) - : owner_{owner}, name_{name}, attrs_{attrs}, details_{std::move(details)} {} + : owner_{owner}, name_{name}, attrs_{attrs}, + details_{std::move(details)} {} const Scope &owner() const { return owner_; } - const Name &name() const { return name_; } + const SourceName &name() { return name_; } Attrs &attrs() { return attrs_; } const Attrs &attrs() const { return attrs_; } @@ -78,6 +82,9 @@ public: // Return a non-owning pointer to details if it is type D, else nullptr. template D *detailsIf() { return std::get_if(&details_); } + template const D *detailsIf() const { + return std::get_if(&details_); + } // Return a reference to the details which must be of type D. template D &details() { @@ -100,7 +107,7 @@ public: private: const Scope &owner_; - const Name name_; + const SourceName name_; Attrs attrs_; Details details_; friend std::ostream &operator<<(std::ostream &, const Symbol &); diff --git a/flang/test/semantics/implicit01.f90 b/flang/test/semantics/implicit01.f90 new file mode 100644 index 0000000..318fe76 --- /dev/null +++ b/flang/test/semantics/implicit01.f90 @@ -0,0 +1,11 @@ +subroutine s1 + implicit none + !ERROR: More than one IMPLICIT NONE statement + implicit none(type) +end subroutine + +subroutine s2 + implicit none(external) + !ERROR: More than one IMPLICIT NONE statement + implicit none +end subroutine diff --git a/flang/test/semantics/implicit02.f90 b/flang/test/semantics/implicit02.f90 new file mode 100644 index 0000000..d77c3f5 --- /dev/null +++ b/flang/test/semantics/implicit02.f90 @@ -0,0 +1,11 @@ +subroutine s1 + implicit none + !ERROR: IMPLICIT statement after IMPLICIT NONE or IMPLICIT NONE(TYPE) statement + implicit integer(a-z) +end subroutine + +subroutine s2 + implicit none(type) + !ERROR: IMPLICIT statement after IMPLICIT NONE or IMPLICIT NONE(TYPE) statement + implicit integer(a-z) +end subroutine diff --git a/flang/test/semantics/implicit03.f90 b/flang/test/semantics/implicit03.f90 new file mode 100644 index 0000000..343471a --- /dev/null +++ b/flang/test/semantics/implicit03.f90 @@ -0,0 +1,11 @@ +subroutine s1 + implicit integer(a-z) + !ERROR: IMPLICIT NONE statement after IMPLICIT statement + implicit none +end subroutine + +subroutine s2 + implicit integer(a-z) + !ERROR: IMPLICIT NONE(TYPE) after IMPLICIT statement + implicit none(type) +end subroutine diff --git a/flang/test/semantics/implicit04.f90 b/flang/test/semantics/implicit04.f90 new file mode 100644 index 0000000..004dbe6 --- /dev/null +++ b/flang/test/semantics/implicit04.f90 @@ -0,0 +1,5 @@ +subroutine s + parameter(a=1.0) + !ERROR: IMPLICIT NONE statement after PARAMETER statement + implicit none +end subroutine diff --git a/flang/test/semantics/implicit05.f90 b/flang/test/semantics/implicit05.f90 new file mode 100644 index 0000000..50039a4 --- /dev/null +++ b/flang/test/semantics/implicit05.f90 @@ -0,0 +1,4 @@ +subroutine s + !ERROR: 'a' does not follow 'b' alphabetically + implicit integer(b-a) +end diff --git a/flang/test/semantics/implicit06.f90 b/flang/test/semantics/implicit06.f90 new file mode 100644 index 0000000..225052c --- /dev/null +++ b/flang/test/semantics/implicit06.f90 @@ -0,0 +1,12 @@ +subroutine s1 + implicit integer(a-c) + !ERROR: More than one implicit type specified for 'c' + implicit real(c-g) +end + +subroutine s2 + implicit integer(a-c) + implicit real(8)(d) + !ERROR: More than one implicit type specified for 'a' + implicit integer(f), real(a) +end diff --git a/flang/test/semantics/resolve01.f90 b/flang/test/semantics/resolve01.f90 new file mode 100644 index 0000000..923e9b0 --- /dev/null +++ b/flang/test/semantics/resolve01.f90 @@ -0,0 +1,4 @@ +integer :: x +!ERROR: 'x' already has a type declared +real :: x +end diff --git a/flang/test/semantics/resolve02.f90 b/flang/test/semantics/resolve02.f90 new file mode 100644 index 0000000..e17e431 --- /dev/null +++ b/flang/test/semantics/resolve02.f90 @@ -0,0 +1,7 @@ +subroutine s + real :: x +contains + !ERROR: 'x' is already declared in this scoping unit + subroutine x + end +end diff --git a/flang/test/semantics/resolve03.f90 b/flang/test/semantics/resolve03.f90 new file mode 100644 index 0000000..63a88f1 --- /dev/null +++ b/flang/test/semantics/resolve03.f90 @@ -0,0 +1,5 @@ +implicit none +integer :: x +!ERROR: No explicit type declared for 'y' +y = x +end diff --git a/flang/test/semantics/resolve04.f90 b/flang/test/semantics/resolve04.f90 new file mode 100644 index 0000000..eca0b0c --- /dev/null +++ b/flang/test/semantics/resolve04.f90 @@ -0,0 +1,10 @@ +!ERROR: No explicit type declared for 'f' +function f() + implicit none +end + +!ERROR: No explicit type declared for 'y' +subroutine s(x, y) + implicit none + integer :: x +end diff --git a/flang/test/semantics/resolve05.f90 b/flang/test/semantics/resolve05.f90 new file mode 100644 index 0000000..20e3f81 --- /dev/null +++ b/flang/test/semantics/resolve05.f90 @@ -0,0 +1,14 @@ +program p + !ERROR: 'p' is already declared in this scoping unit + integer p +end +subroutine s + !ERROR: 's' is already declared in this scoping unit + integer :: s +end +function f() result(res) + integer :: res + !ERROR: 'f' is already declared in this scoping unit + real :: f + res = 1 +end diff --git a/flang/test/semantics/resolve06.f90 b/flang/test/semantics/resolve06.f90 new file mode 100644 index 0000000..12e0e2d --- /dev/null +++ b/flang/test/semantics/resolve06.f90 @@ -0,0 +1,6 @@ +implicit none +allocatable :: x +integer :: x +!ERROR: No explicit type declared for 'y' +allocatable :: y +end -- 2.7.4