diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -698,7 +698,7 @@ Details details_; Symbol() {} // only created in class Symbols - const std::string GetDetailsName() const; + std::string GetDetailsName() const; friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &); friend llvm::raw_ostream &DumpForUnparse( llvm::raw_ostream &, const Symbol &, bool); 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 @@ -53,6 +53,7 @@ const Symbol *FindSubprogram(const Symbol &); const Symbol *FindFunctionResult(const Symbol &); const Symbol *FindOverriddenBinding(const Symbol &); +const Symbol *FindGlobal(const Symbol &); const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &); const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &); 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 @@ -1338,7 +1338,12 @@ bool IsProcedure(const Symbol &symbol) { return common::visit(common::visitors{ - [](const SubprogramDetails &) { return true; }, + [&symbol](const SubprogramDetails &) { + const Scope *scope{symbol.scope()}; + // Main programs & BLOCK DATA are not procedures. + return !scope || + scope->kind() == Scope::Kind::Subprogram; + }, [](const SubprogramNameDetails &) { return true; }, [](const ProcEntityDetails &) { return true; }, [](const GenericDetails &) { return true; }, diff --git a/flang/lib/Semantics/check-call.h b/flang/lib/Semantics/check-call.h --- a/flang/lib/Semantics/check-call.h +++ b/flang/lib/Semantics/check-call.h @@ -30,8 +30,9 @@ // Argument treatingExternalAsImplicit should be true when the called procedure // does not actually have an explicit interface at the call site, but // its characteristics are known because it is a subroutine or function -// defined at the top level in the same source file. -void CheckArguments(const evaluate::characteristics::Procedure &, +// defined at the top level in the same source file. Returns false if +// messages were created, true if all is well. +bool CheckArguments(const evaluate::characteristics::Procedure &, evaluate::ActualArguments &, evaluate::FoldingContext &, const Scope &, bool treatingExternalAsImplicit, const evaluate::SpecificIntrinsic *intrinsic); diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp --- a/flang/lib/Semantics/check-call.cpp +++ b/flang/lib/Semantics/check-call.cpp @@ -960,7 +960,7 @@ .AnyFatalError(); } -void CheckArguments(const characteristics::Procedure &proc, +bool CheckArguments(const characteristics::Procedure &proc, evaluate::ActualArguments &actuals, evaluate::FoldingContext &context, const Scope &scope, bool treatingExternalAsImplicit, const evaluate::SpecificIntrinsic *intrinsic) { @@ -980,21 +980,25 @@ if (auto *msgs{messages.messages()}) { msgs->Annex(std::move(buffer)); } - return; // don't pile on + return false; // don't pile on } } if (explicitInterface) { auto buffer{ CheckExplicitInterface(proc, actuals, context, scope, intrinsic)}; - if (treatingExternalAsImplicit && !buffer.empty()) { - if (auto *msg{messages.Say( - "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) { - buffer.AttachTo(*msg, parser::Severity::Because); + if (!buffer.empty()) { + if (treatingExternalAsImplicit && !buffer.empty()) { + if (auto *msg{messages.Say( + "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) { + buffer.AttachTo(*msg, parser::Severity::Because); + } } - } - if (auto *msgs{messages.messages()}) { - msgs->Annex(std::move(buffer)); + if (auto *msgs{messages.messages()}) { + msgs->Annex(std::move(buffer)); + } + return false; } } + return true; } } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -65,6 +65,7 @@ void CheckArraySpec(const Symbol &, const ArraySpec &); void CheckProcEntity(const Symbol &, const ProcEntityDetails &); void CheckSubprogram(const Symbol &, const SubprogramDetails &); + void CheckLocalVsGlobal(const Symbol &); void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); bool CheckFinal( @@ -103,12 +104,12 @@ return subp && subp->isInterface(); } template - void SayWithDeclaration(const Symbol &symbol, A &&...x) { - if (parser::Message * msg{messages_.Say(std::forward(x)...)}) { - if (messages_.at().begin() != symbol.name().begin()) { - evaluate::AttachDeclaration(*msg, symbol); - } + parser::Message *SayWithDeclaration(const Symbol &symbol, A &&...x) { + parser::Message *msg{messages_.Say(std::forward(x)...)}; + if (msg && messages_.at().begin() != symbol.name().begin()) { + evaluate::AttachDeclaration(*msg, symbol); } + return msg; } bool IsResultOkToDiffer(const FunctionResult &); void CheckBindC(const Symbol &); @@ -199,7 +200,7 @@ const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) { if (type.category() == DeclTypeSpec::Character) { Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters); - } else if (const DerivedTypeSpec * derived{type.AsDerived()}) { + } else if (const DerivedTypeSpec *derived{type.AsDerived()}) { for (auto &parm : derived->parameters()) { Check(parm.second, canHaveAssumedTypeParameters); } @@ -346,7 +347,7 @@ messages_.Say( "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US); } - if (const Symbol * result{FindFunctionResult(symbol)}) { + if (const Symbol *result{FindFunctionResult(symbol)}) { if (IsPointer(*result)) { messages_.Say( "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US); @@ -449,7 +450,7 @@ void CheckHelper::CheckAssumedTypeEntity( // C709 const Symbol &symbol, const ObjectEntityDetails &details) { - if (const DeclTypeSpec * type{symbol.GetType()}; + if (const DeclTypeSpec *type{symbol.GetType()}; type && type->category() == DeclTypeSpec::TypeStar) { if (!IsDummy(symbol)) { messages_.Say( @@ -539,7 +540,7 @@ symbol.name()); } } - if (const DeclTypeSpec * type{details.type()}) { + if (const DeclTypeSpec *type{details.type()}) { if (IsBadCoarrayType(type->AsDerived())) { // C747 & C824 messages_.Say( "Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US, @@ -567,11 +568,11 @@ messages_.Say( "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US); } else if (IsIntentOut(symbol)) { - if (const DeclTypeSpec * type{details.type()}) { + if (const DeclTypeSpec *type{details.type()}) { if (type && type->IsPolymorphic()) { // C1588 messages_.Say( "An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US); - } else if (const DerivedTypeSpec * derived{type->AsDerived()}) { + } else if (const DerivedTypeSpec *derived{type->AsDerived()}) { if (FindUltimateComponent(*derived, [](const Symbol &x) { const DeclTypeSpec *type{x.GetType()}; return type && type->IsPolymorphic(); @@ -661,7 +662,7 @@ "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US); } } - if (const DeclTypeSpec * type{details.type()}) { // C708 + if (const DeclTypeSpec *type{details.type()}) { // C708 if (type->IsPolymorphic() && !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) || IsDummy(symbol))) { @@ -812,7 +813,9 @@ messages_.Say( "An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US); } - const Symbol *interface { details.interface().symbol() }; + const Symbol *interface { + details.interface().symbol() + }; if (!symbol.attrs().test(Attr::INTRINSIC) && (IsElementalProcedure(symbol) || (interface && !interface->attrs().test(Attr::INTRINSIC) && @@ -844,7 +847,7 @@ } if (symbol.attrs().test(Attr::POINTER)) { CheckPointerInitialization(symbol); - if (const Symbol * interface{details.interface().symbol()}) { + if (const Symbol *interface{details.interface().symbol()}) { const Symbol &ultimate{interface->GetUltimate()}; if (ultimate.attrs().test(Attr::INTRINSIC)) { if (const auto intrinsic{ @@ -867,6 +870,7 @@ "Procedure '%s' with SAVE attribute must also have POINTER attribute"_err_en_US, symbol.name()); } + CheckLocalVsGlobal(symbol); } // When a module subprogram has the MODULE prefix the following must match @@ -931,10 +935,10 @@ void CheckHelper::CheckSubprogram( const Symbol &symbol, const SubprogramDetails &details) { - if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) { + if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) { SubprogramMatchHelper{*this}.Check(symbol, *iface); } - if (const Scope * entryScope{details.entryScope()}) { + if (const Scope *entryScope{details.entryScope()}) { // ENTRY 15.6.2.6, esp. C1571 std::optional error; const Symbol *subprogram{entryScope->symbol()}; @@ -980,10 +984,56 @@ } } } - if (details.isInterface() && !details.isDummy() && details.isFunction() && - IsAssumedLengthCharacter(details.result())) { // C721 - messages_.Say(details.result().name(), - "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US); + if (details.isInterface()) { + if (!details.isDummy() && details.isFunction() && + IsAssumedLengthCharacter(details.result())) { // C721 + messages_.Say(details.result().name(), + "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US); + } + } + CheckLocalVsGlobal(symbol); +} + +void CheckHelper::CheckLocalVsGlobal(const Symbol &symbol) { + if (IsProcedure(symbol) && IsExternal(symbol)) { + if (const Symbol *global{FindGlobal(symbol)}; global && global != &symbol) { + std::string interfaceName{symbol.name().ToString()}; + if (const auto *bind{symbol.GetBindName()}) { + interfaceName = *bind; + } + std::string definitionName{global->name().ToString()}; + if (const auto *bind{global->GetBindName()}) { + definitionName = *bind; + } + if (interfaceName == definitionName) { + parser::Message *msg{nullptr}; + if (!IsProcedure(*global)) { + if (symbol.flags().test(Symbol::Flag::Function) || + symbol.flags().test(Symbol::Flag::Subroutine)) { + msg = messages_.Say( + "The global entity '%s' corresponding to the local procedure '%s' is not a callable subprogram"_err_en_US, + global->name(), symbol.name()); + } + } else if (auto chars{Characterize(symbol)}) { + if (auto globalChars{Characterize(*global)}) { + if (chars->HasExplicitInterface()) { + std::string whyNot; + if (!chars->IsCompatibleWith(*globalChars, &whyNot)) { + msg = messages_.Say( + "The global subprogram '%s' is not compatible with its local procedure declaration (%s)"_warn_en_US, + global->name(), whyNot); + } + } else if (!globalChars->CanBeCalledViaImplicitInterface()) { + msg = messages_.Say( + "The global subprogram '%s' may not be referenced via the implicit interface '%s'"_err_en_US, + global->name(), symbol.name()); + } + } + } + evaluate::AttachDeclaration(msg, *global); + evaluate::AttachDeclaration(msg, symbol); + } + } } } @@ -1004,7 +1054,7 @@ (derivedType.attrs().test(Attr::BIND_C) || details.sequence())) { messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US); } - if (const DeclTypeSpec * parent{FindParentTypeSpec(derivedType)}) { + if (const DeclTypeSpec *parent{FindParentTypeSpec(derivedType)}) { const DerivedTypeSpec *parentDerived{parent->AsDerived()}; if (!IsExtensibleType(parentDerived)) { // C705 messages_.Say("The parent type is not extensible"_err_en_US); @@ -1091,7 +1141,7 @@ const Symbol *errSym{&subroutine}; if (const auto *details{subroutine.detailsIf()}) { if (!details->dummyArgs().empty()) { - if (const Symbol * argSym{details->dummyArgs()[0]}) { + if (const Symbol *argSym{details->dummyArgs()[0]}) { errSym = argSym; } } @@ -1230,7 +1280,7 @@ } DistinguishabilityHelper helper{context_}; for (const Symbol &specific : details.specificProcs()) { - if (const Procedure * procedure{Characterize(specific)}) { + if (const Procedure *procedure{Characterize(specific)}) { if (procedure->HasExplicitInterface()) { helper.Add(generic, kind, specific, *procedure); } else { @@ -1573,7 +1623,9 @@ return; } const auto &name{proc.name()}; - const Symbol *interface { interface0 ? FindInterface(*interface0) : nullptr }; + const Symbol *interface { + interface0 ? FindInterface(*interface0) : nullptr + }; if (!interface) { messages_.Say(name, "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US, @@ -1683,7 +1735,7 @@ const Scope &dtScope{symbol.owner()}; CHECK(dtScope.kind() == Scope::Kind::DerivedType); if (symbol.attrs().test(Attr::DEFERRED)) { - if (const Symbol * dtSymbol{dtScope.symbol()}) { + if (const Symbol *dtSymbol{dtScope.symbol()}) { if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733 SayWithDeclaration(*dtSymbol, "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US, @@ -1703,7 +1755,7 @@ "Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US, binding.symbol().name(), symbol.name()); } - if (const Symbol * overridden{FindOverriddenBinding(symbol)}) { + if (const Symbol *overridden{FindOverriddenBinding(symbol)}) { if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) { SayWithDeclaration(*overridden, "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US, @@ -1768,7 +1820,7 @@ void CheckHelper::Check(const Scope &scope) { scope_ = &scope; common::Restorer restorer{innermostSymbol_, innermostSymbol_}; - if (const Symbol * symbol{scope.symbol()}) { + if (const Symbol *symbol{scope.symbol()}) { innermostSymbol_ = symbol; } if (scope.IsParameterizedDerivedTypeInstantiation()) { @@ -1877,7 +1929,7 @@ // Not a generic; ensure characteristics are defined if a function. auto restorer{messages_.SetLocation(generic.name())}; if (IsFunction(generic) && !context_.HasError(generic)) { - if (const Symbol * result{FindFunctionResult(generic)}; + if (const Symbol *result{FindFunctionResult(generic)}; result && !context_.HasError(*result)) { Characterize(generic); } @@ -1893,7 +1945,7 @@ for (std::size_t i{0}; i < specifics.size(); ++i) { const Symbol &specific{*specifics[i]}; auto restorer{messages_.SetLocation(bindingNames[i])}; - if (const Procedure * proc{Characterize(specific)}) { + if (const Procedure *proc{Characterize(specific)}) { if (kind.IsAssignment()) { if (!CheckDefinedAssignment(specific, *proc)) { continue; @@ -1912,7 +1964,7 @@ addSpecifics(symbol); const Symbol &ultimate{symbol.GetUltimate()}; if (ultimate.has()) { - if (const Scope * typeScope{ultimate.scope()}) { + if (const Scope *typeScope{ultimate.scope()}) { for (const auto &pair2 : *typeScope) { addSpecifics(*pair2.second); } @@ -1944,7 +1996,7 @@ "A variable with BIND(C) attribute may only appear in the specification part of a module"_err_en_US); context_.SetError(symbol); } - if (const std::string * name{DefinesBindCName(symbol)}) { + if (const std::string *name{DefinesBindCName(symbol)}) { auto pair{bindC_.emplace(*name, symbol)}; if (!pair.second) { const Symbol &other{*pair.first->second}; @@ -2056,8 +2108,8 @@ void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg, GenericKind::DefinedIo ioKind, const Symbol &generic) { - if (const DeclTypeSpec * type{arg.GetType()}) { - if (const DerivedTypeSpec * derivedType{type->AsDerived()}) { + if (const DeclTypeSpec *type{arg.GetType()}) { + if (const DerivedTypeSpec *derivedType{type->AsDerived()}) { CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic); bool isPolymorphic{type->IsPolymorphic()}; if (isPolymorphic != IsExtensibleType(derivedType)) { @@ -2077,7 +2129,7 @@ void CheckHelper::CheckDioDummyIsDefaultInteger( const Symbol &subp, const Symbol &arg) { - if (const DeclTypeSpec * type{arg.GetType()}; + if (const DeclTypeSpec *type{arg.GetType()}; type && type->IsNumeric(TypeCategory::Integer)) { if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())}; kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) { 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 @@ -64,12 +64,12 @@ static std::optional AnalyzeTypeSpec( const std::optional &spec) { if (spec) { - if (const semantics::DeclTypeSpec * typeSpec{spec->declTypeSpec}) { + if (const semantics::DeclTypeSpec *typeSpec{spec->declTypeSpec}) { // Name resolution sets TypeSpec::declTypeSpec only when it's valid // (viz., an intrinsic type with valid known kind or a non-polymorphic // & non-ABSTRACT derived type). - if (const semantics::IntrinsicTypeSpec * - intrinsic{typeSpec->AsIntrinsic()}) { + if (const semantics::IntrinsicTypeSpec *intrinsic{ + typeSpec->AsIntrinsic()}) { TypeCategory category{intrinsic->category()}; if (auto optKind{ToInt64(intrinsic->kind())}) { int kind{static_cast(*optKind)}; @@ -84,8 +84,8 @@ return DynamicTypeWithLength{DynamicType{category, kind}}; } } - } else if (const semantics::DerivedTypeSpec * - derived{typeSpec->AsDerived()}) { + } else if (const semantics::DerivedTypeSpec *derived{ + typeSpec->AsDerived()}) { return DynamicTypeWithLength{DynamicType{*derived}}; } } @@ -257,7 +257,7 @@ } else if (const auto *object{ symbol.detailsIf()}) { // C928 & C1002 - if (Triplet * last{std::get_if(&ref.subscript().back().u)}) { + if (Triplet *last{std::get_if(&ref.subscript().back().u)}) { if (!last->upper() && object->IsAssumedSize()) { Say("Assumed-size array '%s' must have explicit final " "subscript upper bound value"_err_en_US, @@ -379,10 +379,10 @@ if (auto *triplet{std::get_if( &arrElement.subscripts.front().u)}) { if (!std::get<2 /*stride*/>(triplet->t).has_value()) { - if (const Symbol * - symbol{parser::GetLastName(arrElement.base).symbol}) { + if (const Symbol *symbol{ + parser::GetLastName(arrElement.base).symbol}) { const Symbol &ultimate{symbol->GetUltimate()}; - if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) { + if (const semantics::DeclTypeSpec *type{ultimate.GetType()}) { if (!ultimate.IsObjectArray() && type->category() == semantics::DeclTypeSpec::Character) { // The ambiguous S(j:k) was parsed as an array section @@ -805,8 +805,7 @@ ultimate, AsGenericExpr(TypeParamInquiry{std::nullopt, ultimate}))); } else { if (n.symbol->attrs().test(semantics::Attr::VOLATILE)) { - if (const semantics::Scope * - pure{semantics::FindPureProcedureContaining( + if (const semantics::Scope *pure{semantics::FindPureProcedureContaining( context_.FindScope(n.source))}) { SayAt(n, "VOLATILE variable '%s' may not be referenced in pure subprogram '%s'"_err_en_US, @@ -1068,7 +1067,7 @@ if (ae.subscripts.empty()) { // will be converted to function call later or error reported } else if (baseExpr->Rank() == 0) { - if (const Symbol * symbol{GetLastSymbol(*baseExpr)}) { + if (const Symbol *symbol{GetLastSymbol(*baseExpr)}) { if (!context_.HasError(symbol)) { if (inDataStmtConstant_) { // Better error for NULL(X) with a MOLD= argument @@ -1120,14 +1119,13 @@ if (&component.owner() == &scope) { return Component{std::move(base), component}; } - if (const Symbol * typeSymbol{scope.GetSymbol()}) { - if (const Symbol * - parentComponent{typeSymbol->GetParentComponent(&scope)}) { + if (const Symbol *typeSymbol{scope.GetSymbol()}) { + if (const Symbol *parentComponent{typeSymbol->GetParentComponent(&scope)}) { if (const auto *object{ parentComponent->detailsIf()}) { if (const auto *parentType{object->type()}) { - if (const semantics::Scope * - parentScope{parentType->derivedTypeSpec().scope()}) { + if (const semantics::Scope *parentScope{ + parentType->derivedTypeSpec().scope()}) { return CreateComponent( DataRef{Component{std::move(base), *parentComponent}}, component, *parentScope); @@ -1227,7 +1225,7 @@ if (auto *aRef{std::get_if(&dataRef->u)}) { subscripts = std::move(aRef->subscript()); reversed.push_back(aRef->GetLastSymbol()); - if (Component * component{aRef->base().UnwrapComponent()}) { + if (Component *component{aRef->base().UnwrapComponent()}) { dataRef = &component->base(); } else { dataRef = nullptr; @@ -1669,7 +1667,7 @@ auto &parsedType{std::get(structure.t)}; parser::Name structureType{std::get(parsedType.t)}; parser::CharBlock &typeName{structureType.source}; - if (semantics::Symbol * typeSymbol{structureType.symbol}) { + if (semantics::Symbol *typeSymbol{structureType.symbol}) { if (typeSymbol->has()) { semantics::DerivedTypeSpec dtSpec{typeName, typeSymbol->GetUltimate()}; if (!CheckIsValidForwardReference(dtSpec)) { @@ -1814,9 +1812,9 @@ } else if (symbol->has()) { // C1594(4) if (const auto *pureProc{FindPureProcedureContaining(innermost)}) { - if (const Symbol * pointer{FindPointerComponent(*symbol)}) { - if (const Symbol * - object{FindExternallyVisibleObject(*value, *pureProc)}) { + if (const Symbol *pointer{FindPointerComponent(*symbol)}) { + if (const Symbol *object{ + FindExternallyVisibleObject(*value, *pureProc)}) { if (auto *msg{Say(expr.source, "Externally visible object '%s' may not be " "associated with pointer component '%s' in a " @@ -1954,7 +1952,9 @@ static int GetPassIndex(const Symbol &proc) { CHECK(!proc.attrs().test(semantics::Attr::NOPASS)); std::optional passName{GetPassName(proc)}; - const auto *interface { semantics::FindInterface(proc) }; + const auto *interface { + semantics::FindInterface(proc) + }; if (!passName || !interface) { return 0; // first argument is passed-object } @@ -2019,7 +2019,7 @@ bool isSubroutine) -> std::optional { const parser::StructureComponent &sc{pcr.v.thing}; if (MaybeExpr base{Analyze(sc.base)}) { - if (const Symbol * sym{sc.component.symbol}) { + if (const Symbol *sym{sc.component.symbol}) { if (context_.HasError(sym)) { return std::nullopt; } @@ -2053,8 +2053,8 @@ if (dataRef && !CheckDataRef(*dataRef)) { return std::nullopt; } - if (const Symbol * - resolution{GetBindingResolution(dtExpr->GetType(), *sym)}) { + if (const Symbol *resolution{ + GetBindingResolution(dtExpr->GetType(), *sym)}) { AddPassArg(arguments, std::move(*dtExpr), *sym, false); return CalleeAndArguments{ ProcedureDesignator{*resolution}, std::move(arguments)}; @@ -2231,7 +2231,7 @@ } // Check parent derived type if (const auto *parentScope{symbol.owner().GetDerivedTypeParent()}) { - if (const Symbol * extended{parentScope->FindComponent(symbol.name())}) { + if (const Symbol *extended{parentScope->FindComponent(symbol.name())}) { auto pair{ResolveGeneric( *extended, actuals, adjustActuals, isSubroutine, false)}; if (pair.first) { @@ -2247,7 +2247,7 @@ // See 15.5.5.2 for details. if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) { for (const std::string &n : GetAllNames(context_, symbol.name())) { - if (const Symbol * outer{symbol.owner().parent().FindSymbol(n)}) { + if (const Symbol *outer{symbol.owner().parent().FindSymbol(n)}) { auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine, mightBeStructureConstructor)}; if (pair.first) { @@ -2451,7 +2451,7 @@ } template <> const Symbol *AssumedTypeDummy(const parser::Name &name) { - if (const Symbol * symbol{name.symbol}) { + if (const Symbol *symbol{name.symbol}) { if (const auto *type{symbol->GetType()}) { if (type->category() == semantics::DeclTypeSpec::TypeStar) { return symbol; @@ -2670,21 +2670,22 @@ std::optional ExpressionAnalyzer::CheckCall( parser::CharBlock callSite, const ProcedureDesignator &proc, ActualArguments &arguments) { + bool treatExternalAsImplicit{IsExternalCalledImplicitly(callSite, proc)}; + const Symbol *procSymbol{proc.GetSymbol()}; auto chars{characteristics::Procedure::Characterize( proc, context_.foldingContext())}; + bool ok{true}; if (chars) { - bool treatExternalAsImplicit{IsExternalCalledImplicitly(callSite, proc)}; if (treatExternalAsImplicit && !chars->CanBeCalledViaImplicitInterface()) { Say(callSite, "References to the procedure '%s' require an explicit interface"_err_en_US, - DEREF(proc.GetSymbol()).name()); + DEREF(procSymbol).name()); } // Checks for ASSOCIATED() are done in intrinsic table processing const SpecificIntrinsic *specificIntrinsic{proc.GetSpecificIntrinsic()}; bool procIsAssociated{ specificIntrinsic && specificIntrinsic->name == "associated"}; if (!procIsAssociated) { - const Symbol *procSymbol{proc.GetSymbol()}; bool procIsDummy{procSymbol && IsDummy(*procSymbol)}; if (chars->functionResult && chars->functionResult->IsAssumedLengthCharacter() && @@ -2692,12 +2693,11 @@ Say(callSite, "Assumed-length character function must be defined with a length to be called"_err_en_US); } - semantics::CheckArguments(*chars, arguments, GetFoldingContext(), + ok &= semantics::CheckArguments(*chars, arguments, GetFoldingContext(), context_.FindScope(callSite), treatExternalAsImplicit, specificIntrinsic); if (procSymbol && !IsPureProcedure(*procSymbol)) { - if (const semantics::Scope * - pure{semantics::FindPureProcedureContaining( + if (const semantics::Scope *pure{semantics::FindPureProcedureContaining( context_.FindScope(callSite))}) { Say(callSite, "Procedure '%s' referenced in pure subprogram '%s' must be pure too"_err_en_US, @@ -2706,6 +2706,19 @@ } } } + if (ok && !treatExternalAsImplicit && procSymbol && + !(chars && chars->HasExplicitInterface())) { + if (const Symbol *global{FindGlobal(*procSymbol)}; + global && global != procSymbol && IsProcedure(*global)) { + // Check a known global definition behind a local interface + if (auto globalChars{characteristics::Procedure::Characterize( + *global, context_.foldingContext())}) { + semantics::CheckArguments(*globalChars, arguments, GetFoldingContext(), + context_.FindScope(callSite), true, + nullptr /*not specific intrinsic*/); + } + } + } return chars; } @@ -2713,8 +2726,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) { if (MaybeExpr operand{Analyze(x.v.value())}) { - if (const semantics::Symbol * symbol{GetLastSymbol(*operand)}) { - if (const semantics::Symbol * result{FindFunctionResult(*symbol)}) { + if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) { + if (const semantics::Symbol *result{FindFunctionResult(*symbol)}) { if (semantics::IsProcedurePointer(*result)) { Say("A function reference that returns a procedure " "pointer may not be parenthesized"_err_en_US); // C1003 @@ -2782,7 +2795,7 @@ // intrinsic function. // Use the actual source for the name of the call for error reporting. std::optional arg; - if (const Symbol * assumedTypeDummy{AssumedTypeDummy(x.v.value())}) { + if (const Symbol *assumedTypeDummy{AssumedTypeDummy(x.v.value())}) { arg = ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}}; } else if (MaybeExpr argExpr{Analyze(x.v.value())}) { arg = ActualArgument{std::move(*argExpr)}; @@ -3018,8 +3031,7 @@ if (!name->symbol) { return false; } else if (name->symbol->Rank() == 0) { - if (const Symbol * - function{ + if (const Symbol *function{ semantics::IsFunctionResultWithSameNameAsFunction(*name->symbol)}) { auto &msg{context.Say(funcRef.v.source, "Recursive call to '%s' requires a distinct RESULT in its declaration"_err_en_US, @@ -3055,8 +3067,7 @@ std::get_if>(&u)}) { parser::FunctionReference &funcRef{func->value()}; auto &proc{std::get(funcRef.v.t)}; - if (Symbol * - origSymbol{ + if (Symbol *origSymbol{ common::visit(common::visitors{ [&](parser::Name &name) { return name.symbol; }, [&](parser::ProcComponentRef &pcr) { @@ -3343,7 +3354,7 @@ return Expr{NullPointer{}}; } } - if (const Symbol * symbol{proc.GetSymbol()}) { + if (const Symbol *symbol{proc.GetSymbol()}) { if (!ResolveForward(*symbol)) { return std::nullopt; } @@ -3578,7 +3589,7 @@ isUserOp ? std::string{opr} : "operator("s + opr + ')'}; parser::CharBlock oprName{oprNameString}; const auto &scope{context_.context().FindScope(source_)}; - if (Symbol * symbol{scope.FindSymbol(oprName)}) { + if (Symbol *symbol{scope.FindSymbol(oprName)}) { *definedOpSymbolPtr = symbol; parser::Name name{symbol->name(), symbol}; if (auto result{context_.AnalyzeDefinedOp(name, GetActuals())}) { @@ -3586,8 +3597,8 @@ } } for (std::size_t passIndex{0}; passIndex < actuals_.size(); ++passIndex) { - if (const Symbol * - symbol{FindBoundOp(oprName, passIndex, *definedOpSymbolPtr)}) { + if (const Symbol *symbol{ + FindBoundOp(oprName, passIndex, *definedOpSymbolPtr)}) { if (MaybeExpr result{TryBoundOp(*symbol, passIndex)}) { return result; } @@ -3699,7 +3710,7 @@ parser::CharBlock oprName{oprNameString}; const Symbol *proc{nullptr}; const auto &scope{context_.context().FindScope(source_)}; - if (const Symbol * symbol{scope.FindSymbol(oprName)}) { + if (const Symbol *symbol{scope.FindSymbol(oprName)}) { ExpressionAnalyzer::AdjustActuals noAdjustment; auto pair{context_.ResolveGeneric(*symbol, actuals_, noAdjustment, true)}; if (pair.first) { @@ -3711,9 +3722,9 @@ int passedObjectIndex{-1}; const Symbol *definedOpSymbol{nullptr}; for (std::size_t i{0}; i < actuals_.size(); ++i) { - if (const Symbol * specific{FindBoundOp(oprName, i, definedOpSymbol)}) { - if (const Symbol * - resolution{GetBindingResolution(GetType(i), *specific)}) { + if (const Symbol *specific{FindBoundOp(oprName, i, definedOpSymbol)}) { + if (const Symbol *resolution{ + GetBindingResolution(GetType(i), *specific)}) { proc = resolution; } else { proc = specific; @@ -3737,7 +3748,7 @@ for (const auto &actual : actuals_) { if (!actual.has_value()) { os << "- error\n"; - } else if (const Symbol * symbol{actual->GetAssumedTypeDummy()}) { + } else if (const Symbol *symbol{actual->GetAssumedTypeDummy()}) { os << "- assumed type: " << symbol->name().ToString() << '\n'; } else if (const Expr *expr{actual->UnwrapExpr()}) { expr->AsFortran(os << "- expr: ") << '\n'; @@ -3750,7 +3761,7 @@ std::optional ArgumentAnalyzer::AnalyzeExpr( const parser::Expr &expr) { source_.ExtendToCover(expr.source); - if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) { + if (const Symbol *assumedTypeDummy{AssumedTypeDummy(expr)}) { expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter); if (isProcedureCall_) { ActualArgument arg{ActualArgument::AssumedType{*assumedTypeDummy}}; diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -253,9 +253,7 @@ details); } -const std::string Symbol::GetDetailsName() const { - return DetailsToString(details_); -} +std::string Symbol::GetDetailsName() const { return DetailsToString(details_); } void Symbol::set_details(Details &&details) { CHECK(CanReplaceDetails(details)); 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 @@ -520,6 +520,36 @@ return nullptr; } +const Symbol *FindGlobal(const Symbol &original) { + const Symbol &ultimate{original.GetUltimate()}; + if (ultimate.owner().IsGlobal()) { + return &ultimate; + } + bool isLocal{false}; + if (IsDummy(ultimate)) { + } else if (IsPointer(ultimate)) { + } else if (ultimate.has()) { + isLocal = IsExternal(ultimate); + } else if (const auto *subp{ultimate.detailsIf()}) { + isLocal = subp->isInterface(); + } + if (isLocal) { + const std::string *bind{ultimate.GetBindName()}; + if (!bind || ultimate.name() == *bind) { + const Scope &globalScope{ultimate.owner().context().globalScope()}; + if (auto iter{globalScope.find(ultimate.name())}; + iter != globalScope.end()) { + const Symbol &global{*iter->second}; + const std::string *globalBind{global.GetBindName()}; + if (!globalBind || global.name() == *globalBind) { + return &global; + } + } + } + } + return nullptr; +} + const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) { return FindParentTypeSpec(derived.typeSymbol()); } diff --git a/flang/test/Semantics/global01.f90 b/flang/test/Semantics/global01.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/global01.f90 @@ -0,0 +1,45 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror +! Catch discrepancies between a local interface and a global definition + +subroutine global1(x) + integer, intent(in) :: x +end subroutine + +subroutine global2(x) bind(c,name="xyz") + integer, intent(in) :: x +end subroutine + +subroutine global3(x) + integer, intent(in) :: x +end subroutine + +pure subroutine global4(x) + integer, intent(in) :: x +end subroutine + +subroutine global5(x) + integer, intent(in) :: x +end subroutine + +program test + interface + !WARNING: The global subprogram 'global1' is not compatible with its local procedure declaration (incompatible dummy argument #1: incompatible dummy data object types: REAL(4) vs INTEGER(4)) + subroutine global1(x) + real, intent(in) :: x + end subroutine + subroutine global2(x) + real, intent(in) :: x + end subroutine + subroutine global3(x) bind(c,name="abc") + real, intent(in) :: x + end subroutine + subroutine global4(x) ! not PURE, but that's ok + integer, intent(in) :: x + end subroutine + !WARNING: The global subprogram 'global5' is not compatible with its local procedure declaration (incompatible procedure attributes: Pure) + pure subroutine global5(x) + integer, intent(in) :: x + end subroutine + end interface +end + diff --git a/flang/test/Semantics/local-vs-global.f90 b/flang/test/Semantics/local-vs-global.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/local-vs-global.f90 @@ -0,0 +1,164 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 + +module module_before_1 +end + +module module_before_2 +end + +block data block_data_before_1 +end + +block data block_data_before_2 +end + +subroutine explicit_before_1(a) + real, optional :: a +end + +subroutine explicit_before_2(a) + real, optional :: a +end + +subroutine implicit_before_1(a) + real :: a +end + +subroutine implicit_before_2(a) + real :: a +end + +function explicit_func_before_1(a) + real, optional :: a +end + +function explicit_func_before_2(a) + real, optional :: a +end + +function implicit_func_before_1(a) + real :: a +end + +function implicit_func_before_2(a) + real :: a +end + +program test + external justfine ! OK to name a BLOCK DATA if not called + !ERROR: The global entity 'module_before_1' corresponding to the local procedure 'module_before_1' is not a callable subprogram + external module_before_1 + !ERROR: The global entity 'block_data_before_1' corresponding to the local procedure 'block_data_before_1' is not a callable subprogram + external block_data_before_1 + !ERROR: The global subprogram 'explicit_before_1' may not be referenced via the implicit interface 'explicit_before_1' + external explicit_before_1 + external implicit_before_1 + !ERROR: The global subprogram 'explicit_func_before_1' may not be referenced via the implicit interface 'explicit_func_before_1' + external explicit_func_before_1 + external implicit_func_before_1 + !ERROR: The global entity 'module_after_1' corresponding to the local procedure 'module_after_1' is not a callable subprogram + external module_after_1 + !ERROR: The global entity 'block_data_after_1' corresponding to the local procedure 'block_data_after_1' is not a callable subprogram + external block_data_after_1 + !ERROR: The global subprogram 'explicit_after_1' may not be referenced via the implicit interface 'explicit_after_1' + external explicit_after_1 + external implicit_after_1 + !ERROR: The global subprogram 'explicit_func_after_1' may not be referenced via the implicit interface 'explicit_func_after_1' + external explicit_func_after_1 + external implicit_func_after_1 + call module_before_1 + !ERROR: 'module_before_2' is not a callable procedure + call module_before_2 + call block_data_before_1 + !ERROR: 'block_data_before_2' is not a callable procedure + call block_data_before_2 + call explicit_before_1(1.) + !ERROR: References to the procedure 'explicit_before_2' require an explicit interface + call explicit_before_2(1.) + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + call implicit_before_1 + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + call implicit_before_2 + print *, explicit_func_before_1(1.) + !ERROR: References to the procedure 'explicit_func_before_2' require an explicit interface + print *, explicit_func_before_2(1.) + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + print *, implicit_func_before_1() + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + print *, implicit_func_before_2() + call module_after_1 + call module_after_2 + call block_data_after_1 + call block_data_after_2 + call explicit_after_1(1.) + !ERROR: References to the procedure 'explicit_after_2' require an explicit interface + call explicit_after_2(1.) + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + call implicit_after_1 + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + call implicit_after_2 + print *, explicit_func_after_1(1.) + !ERROR: References to the procedure 'explicit_func_after_2' require an explicit interface + print *, explicit_func_after_2(1.) + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + print *, implicit_func_after_1() + !WARNING: If the procedure's interface were explicit, this reference would be in error + !BECAUSE: Dummy argument 'a=' (#1) is not OPTIONAL and is not associated with an actual argument in this procedure reference + print *, implicit_func_after_2() +end program + +block data justfine +end + +module module_after_1 +end + +!ERROR: 'module_after_2' is already declared in this scoping unit +module module_after_2 +end + +block data block_data_after_1 +end + +!ERROR: BLOCK DATA 'block_data_after_2' has been called +block data block_data_after_2 +end + +subroutine explicit_after_1(a) + real, optional :: a +end + +subroutine explicit_after_2(a) + real, optional :: a +end + +subroutine implicit_after_1(a) + real :: a +end + +subroutine implicit_after_2(a) + real :: a +end + +function explicit_func_after_1(a) + real, optional :: a +end + +function explicit_func_after_2(a) + real, optional :: a +end + +function implicit_func_after_1(a) + real :: a +end + +function implicit_func_after_2(a) + real :: a +end diff --git a/flang/test/Semantics/procinterface01.f90 b/flang/test/Semantics/procinterface01.f90 --- a/flang/test/Semantics/procinterface01.f90 +++ b/flang/test/Semantics/procinterface01.f90 @@ -130,9 +130,9 @@ end function nested5 end module module1 -!DEF: /explicit1 ELEMENTAL (Function) Subprogram REAL(4) +!DEF: /explicit1 (Function) Subprogram REAL(4) !DEF: /explicit1/x INTENT(IN) ObjectEntity REAL(4) -real elemental function explicit1(x) +real function explicit1(x) !REF: /explicit1/x real, intent(in) :: x !DEF: /explicit1/explicit1 ObjectEntity REAL(4) @@ -150,14 +150,13 @@ logical = x+3. end function logical -!DEF: /tan (Function) Subprogram REAL(4) +!DEF: /tan (Function) Subprogram CHARACTER(1_8,1) !DEF: /tan/x INTENT(IN) ObjectEntity REAL(4) -real function tan(x) +character*1 function tan(x) !REF: /tan/x real, intent(in) :: x - !DEF: /tan/tan ObjectEntity REAL(4) - !REF: /tan/x - tan = x+5. + !DEF: /tan/tan ObjectEntity CHARACTER(1_8,1) + tan = "?" end function tan !DEF: /main MainProgram diff --git a/flang/test/Semantics/resolve102.f90 b/flang/test/Semantics/resolve102.f90 --- a/flang/test/Semantics/resolve102.f90 +++ b/flang/test/Semantics/resolve102.f90 @@ -30,6 +30,7 @@ !ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2' procedure(sub) :: p interface + !ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2' subroutine sub(p2) import p procedure(p) :: p2 diff --git a/flang/test/Semantics/resolve53.f90 b/flang/test/Semantics/resolve53.f90 --- a/flang/test/Semantics/resolve53.f90 +++ b/flang/test/Semantics/resolve53.f90 @@ -97,7 +97,6 @@ end subroutine end interface end - ! Two procedures that differ only by attributes are not distinguishable module m8 @@ -468,7 +467,7 @@ end interface end module -subroutine s1() +subroutine subr1() use m20 interface operator(.not.) !ERROR: Procedure 'f' from module 'm20' is already specified in generic 'OPERATOR(.NOT.)' @@ -478,7 +477,7 @@ !ERROR: Procedure 'f' from module 'm20' is already specified in generic 'OPERATOR(+)' procedure f end interface -end subroutine s1 +end subroutine subr1 ! Extensions for distinguishable allocatable arguments; these should not ! elicit errors from f18 diff --git a/flang/test/Semantics/resolve62.f90 b/flang/test/Semantics/resolve62.f90 --- a/flang/test/Semantics/resolve62.f90 +++ b/flang/test/Semantics/resolve62.f90 @@ -1,6 +1,6 @@ ! RUN: %python %S/test_errors.py %s %flang_fc1 ! Resolve generic based on number of arguments -subroutine s1 +subroutine subr1 interface f real function f1(x) optional :: x @@ -15,7 +15,7 @@ end ! Elemental and non-element function both match: non-elemental one should be used -subroutine s2 +subroutine subr2 interface f logical elemental function f1(x) intent(in) :: x @@ -53,10 +53,10 @@ real, protected :: x real :: y interface s - pure subroutine s1(x) + pure subroutine s101(x) real, intent(out) :: x end - subroutine s2(x, y) + subroutine s102(x, y) real :: x, y end end interface