[flang] Fix bugs with use-associated derived type with rename

When a derived type is use-associated with a rename, like
`use m, only: t2 => t1`
we need to record in the `DerivedTypeSpec` both the local-name in this
scope and the symbol for the derived type.

In most cases we need to work the the type symbol and its
`DerivedTypeDetails`, but when writing the type to the module file
we need the local-name. The name of the type symbol may be hidden
or not use-associated.

When analyzing a `parser::Name` we don't want to follow use-associations
because we could end up with the wrong name in a `DataRef` (i.e. the
use-name rather than the local-name). But that means that
`GetNamedConstantValue()` does have to follow them or named constants
won't always be folded.

Fixes flang-compiler/f18#729.

Original-commit: flang-compiler/f18@50d8921c69
Reviewed-on: https://github.com/flang-compiler/f18/pull/740
This commit is contained in:
Tim Keith 2019-09-12 13:43:16 -07:00 committed by GitHub
parent 0b86ab186d
commit a29678ddb6
9 changed files with 154 additions and 74 deletions

View File

@ -1184,7 +1184,7 @@ Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
template<typename T>
std::optional<Expr<T>> GetNamedConstantValue(
FoldingContext &context, const Symbol &symbol0) {
const Symbol &symbol{ResolveAssociations(symbol0)};
const Symbol &symbol{ResolveAssociations(symbol0).GetUltimate()};
if (IsNamedConstant(symbol)) {
if (const auto *object{
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {

View File

@ -441,7 +441,7 @@ std::string SomeDerived::AsFortran() const {
std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) {
if (spec.HasActualParameters()) {
std::stringstream ss;
ss << spec.typeSymbol().name().ToString();
ss << spec.name().ToString();
char ch{'('};
for (const auto &[name, value] : spec.parameters()) {
ss << ch << name.ToString() << '=';
@ -457,7 +457,7 @@ std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) {
ss << ')';
return ss.str();
} else {
return spec.typeSymbol().name().ToString();
return spec.name().ToString();
}
}

View File

@ -601,19 +601,12 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
return std::nullopt;
} else {
const Symbol &ultimate{n.symbol->GetUltimate()};
if (ultimate.detailsIf<semantics::TypeParamDetails>()) {
if (ultimate.has<semantics::TypeParamDetails>()) {
// A bare reference to a derived type parameter (within a parameterized
// derived type definition)
return AsMaybeExpr(MakeBareTypeParamInquiry(&ultimate));
} else {
const auto *details{ultimate.detailsIf<semantics::ObjectEntityDetails>()};
if (details && semantics::IsNamedConstant(ultimate) && details->type() &&
details->type()->AsIntrinsic() && !details->IsArray() &&
details->init()) {
return details->init();
} else {
return Designate(DataRef{*n.symbol});
}
return Designate(DataRef{*n.symbol});
}
}
}
@ -2006,7 +1999,7 @@ static void FixMisparsedFunctionReference(
CheckFuncRefToArrayElementRefHasSubscripts(context, funcRef);
u = common::Indirection{funcRef.ConvertToArrayElementRef()};
} else {
common::die("can't fix misparsed function as array reference");
DIE("can't fix misparsed function as array reference");
}
} else if (const auto *name{std::get_if<parser::Name>(&proc.u)}) {
// A procedure component reference can't be a structure
@ -2021,15 +2014,15 @@ static void FixMisparsedFunctionReference(
if (derivedType != nullptr) {
if constexpr (common::HasMember<parser::StructureConstructor,
uType>) {
CHECK(derivedType->has<semantics::DerivedTypeDetails>());
auto &scope{context.FindScope(name->source)};
const semantics::DeclTypeSpec &type{
semantics::FindOrInstantiateDerivedType(
scope, semantics::DerivedTypeSpec{*derivedType}, context)};
semantics::FindOrInstantiateDerivedType(scope,
semantics::DerivedTypeSpec{
origSymbol->name(), *derivedType},
context)};
u = funcRef.ConvertToStructureConstructor(type.derivedTypeSpec());
} else {
common::die(
"can't fix misparsed function as structure constructor");
DIE("can't fix misparsed function as structure constructor");
}
}
}

View File

@ -68,8 +68,8 @@ public:
SubprogramSymbolCollector(const Symbol &symbol)
: symbol_{symbol}, scope_{*symbol.scope()} {}
SymbolVector symbols() const { return need_; }
SymbolSet imports() const { return imports_; }
const SymbolVector &symbols() const { return need_; }
const std::set<SourceName> &imports() const { return imports_; }
void Collect();
private:
@ -79,13 +79,14 @@ private:
SymbolVector need_; // symbols that are needed
SymbolSet needSet_; // symbols already in need_
SymbolSet useSet_; // use-associations that might be needed
SymbolSet imports_; // imports from host that are needed
std::set<SourceName> imports_; // imports from host that are needed
void DoSymbol(const Symbol &);
void DoSymbol(const SourceName &, const Symbol &);
void DoType(const DeclTypeSpec *);
void DoBound(const Bound &);
void DoParamValue(const ParamValue &);
bool NeedImport(const Symbol &);
bool NeedImport(const SourceName &, const Symbol &);
struct SymbolVisitor : public virtual evaluate::VisitorBase<SymbolVector> {
using Result = SymbolVector;
@ -253,7 +254,7 @@ void ModFileWriter::PutDerivedType(const Symbol &typeSymbol) {
auto &details{typeSymbol.get<DerivedTypeDetails>()};
PutAttrs(decls_ << "type", typeSymbol.attrs());
if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
decls_ << ",extends(" << extends->typeSymbol().name() << ')';
decls_ << ",extends(" << extends->name() << ')';
}
decls_ << "::" << typeSymbol.name();
auto &typeScope{*typeSymbol.scope()};
@ -329,8 +330,8 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) {
}
CHECK(typeBindings.str().empty());
os << writer.uses_.str();
for (const Symbol *import : collector.imports()) {
decls_ << "import::" << import->name().ToString() << "\n";
for (const SourceName &import : collector.imports()) {
decls_ << "import::" << import << "\n";
}
os << writer.decls_.str();
os << "end\n";
@ -811,15 +812,20 @@ void SubprogramSymbolCollector::Collect() {
}
}
// Do symbols this one depends on; then add to need_
void SubprogramSymbolCollector::DoSymbol(const Symbol &symbol) {
DoSymbol(symbol.name(), symbol);
}
// Do symbols this one depends on; then add to need_
void SubprogramSymbolCollector::DoSymbol(
const SourceName &name, const Symbol &symbol) {
const auto &scope{symbol.owner()};
if (scope != scope_ && !scope.IsDerivedType()) {
if (scope != scope_.parent()) {
useSet_.insert(&symbol);
}
if (NeedImport(symbol)) {
imports_.insert(&symbol);
if (NeedImport(name, symbol)) {
imports_.insert(name);
}
return;
}
@ -871,7 +877,7 @@ void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
const auto &typeSymbol{derived->typeSymbol()};
if (const DerivedTypeSpec * extends{typeSymbol.GetParentTypeSpec()}) {
DoSymbol(extends->typeSymbol());
DoSymbol(extends->name(), extends->typeSymbol());
}
for (const auto pair : derived->parameters()) {
DoParamValue(pair.second);
@ -880,7 +886,7 @@ void SubprogramSymbolCollector::DoType(const DeclTypeSpec *type) {
const auto &comp{*pair.second};
DoSymbol(comp);
}
DoSymbol(typeSymbol);
DoSymbol(derived->name(), derived->typeSymbol());
}
}
}
@ -897,12 +903,13 @@ void SubprogramSymbolCollector::DoParamValue(const ParamValue &paramValue) {
}
// Do we need a IMPORT of this symbol into an interface block?
bool SubprogramSymbolCollector::NeedImport(const Symbol &symbol) {
bool SubprogramSymbolCollector::NeedImport(
const SourceName &name, const Symbol &symbol) {
if (!isInterface_) {
return false;
} else if (symbol.owner() != scope_.parent()) {
// detect import from parent of use-associated symbol
const auto *found{scope_.FindSymbol(symbol.name())};
const auto *found{scope_.FindSymbol(name)};
return DEREF(found).has<UseDetails>() && found->owner() != scope_;
} else {
return true;

View File

@ -859,7 +859,9 @@ private:
Symbol &DeclareUnknownEntity(const parser::Name &, Attrs);
Symbol &DeclareProcEntity(const parser::Name &, Attrs, const ProcInterface &);
void SetType(const parser::Name &, const DeclTypeSpec &);
const Symbol *ResolveDerivedType(const parser::Name &);
std::optional<DerivedTypeSpec> ResolveDerivedType(const parser::Name &);
std::optional<DerivedTypeSpec> ResolveExtendsType(
const parser::Name &, const parser::Name *);
Symbol *MakeTypeSymbol(const SourceName &, Details &&);
Symbol *MakeTypeSymbol(const parser::Name &, Details &&);
bool OkToAddComponent(const parser::Name &, const Symbol * = nullptr);
@ -3146,10 +3148,11 @@ bool DeclarationVisitor::Pre(const parser::DeclarationTypeSpec::Record &) {
void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
const auto &typeName{std::get<parser::Name>(x.t)};
const Symbol *typeSymbol{ResolveDerivedType(typeName)};
if (typeSymbol == nullptr) {
auto spec{ResolveDerivedType(typeName)};
if (!spec) {
return;
}
const Symbol *typeSymbol{&spec->typeSymbol()};
// This DerivedTypeSpec is created initially as a search key.
// If it turns out to have the same name and actual parameter
@ -3157,7 +3160,6 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
// scope, then we'll use that extant spec; otherwise, when this
// spec is distinct from all derived types previously instantiated
// in the current scope, this spec will be moved to that collection.
DerivedTypeSpec spec{*typeSymbol};
// The expressions in a derived type specifier whose values define
// non-defaulted type parameters are evaluated in the enclosing scope.
@ -3204,14 +3206,14 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
"Too many type parameters given for derived type '%s'"_err_en_US);
break;
}
if (spec.FindParameter(name)) {
if (spec->FindParameter(name)) {
Say(typeName.source,
"Multiple values given for type parameter '%s'"_err_en_US, name);
} else {
const auto &value{std::get<parser::TypeParamValue>(typeParamSpec.t)};
ParamValue param{GetParamValue(value, attr)}; // folded
if (!param.isExplicit() || param.GetExplicit().has_value()) {
spec.AddParamValue(name, std::move(param));
spec->AddParamValue(name, std::move(param));
}
}
}
@ -3221,7 +3223,7 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
const Scope *typeScope{typeSymbol->scope()};
CHECK(typeScope != nullptr);
for (const SourceName &name : parameterNames) {
if (!spec.FindParameter(name)) {
if (!spec->FindParameter(name)) {
auto it{std::find_if(parameterDecls.begin(), parameterDecls.end(),
[&](const Symbol *symbol) { return symbol->name() == name; })};
if (it != parameterDecls.end()) {
@ -3236,14 +3238,14 @@ void DeclarationVisitor::Post(const parser::DerivedTypeSpec &x) {
}
auto category{GetDeclTypeSpecCategory()};
ProcessParameterExpressions(spec, context().foldingContext());
ProcessParameterExpressions(*spec, context().foldingContext());
if (const DeclTypeSpec *
extant{currScope().FindInstantiatedDerivedType(spec, category)}) {
extant{currScope().FindInstantiatedDerivedType(*spec, category)}) {
// This derived type and parameter expressions (if any) are already present
// in this scope.
SetDeclTypeSpec(*extant);
} else {
DeclTypeSpec &type{currScope().MakeDerivedType(category, std::move(spec))};
DeclTypeSpec &type{currScope().MakeDerivedType(category, std::move(*spec))};
if (parameterNames.empty() || currScope().IsParameterizedDerivedType()) {
// The derived type being instantiated is not a parameterized derived
// type, or the instantiation is within the definition of a parameterized
@ -3326,33 +3328,28 @@ void DeclarationVisitor::Post(const parser::DerivedTypeStmt &x) {
// Resolve the EXTENDS() clause before creating the derived
// type's symbol to foil attempts to recursively extend a type.
auto *extendsName{derivedTypeInfo_.extends};
const Symbol *extendsType{nullptr};
if (extendsName != nullptr) {
if (extendsName->source == name.source) {
Say(extendsName->source,
"Derived type '%s' cannot extend itself"_err_en_US);
} else {
extendsType = ResolveDerivedType(*extendsName);
}
}
std::optional<DerivedTypeSpec> extendsType{
ResolveExtendsType(name, extendsName)};
auto &symbol{MakeSymbol(name, GetAttrs(), DerivedTypeDetails{})};
symbol.ReplaceName(name.source);
derivedTypeInfo_.type = &symbol;
PushScope(Scope::Kind::DerivedType, &symbol);
if (extendsType != nullptr) {
if (extendsType.has_value()) {
// Declare the "parent component"; private if the type is
// Any symbol stored in the EXTENDS() clause is temporarily
// hidden so that a new symbol can be created for the parent
// component without producing spurious errors about already
// existing.
const Symbol &extendsSymbol{extendsType->typeSymbol()};
auto restorer{common::ScopedSet(extendsName->symbol, nullptr)};
if (OkToAddComponent(*extendsName, extendsType)) {
if (OkToAddComponent(*extendsName, &extendsSymbol)) {
auto &comp{DeclareEntity<ObjectEntityDetails>(*extendsName, Attrs{})};
comp.attrs().set(Attr::PRIVATE, extendsType->attrs().test(Attr::PRIVATE));
comp.attrs().set(
Attr::PRIVATE, extendsSymbol.attrs().test(Attr::PRIVATE));
comp.set(Symbol::Flag::ParentComp);
DeclTypeSpec &type{currScope().MakeDerivedType(
DeclTypeSpec::TypeDerived, DerivedTypeSpec{*extendsType})};
type.derivedTypeSpec().set_scope(*extendsType->scope());
DeclTypeSpec::TypeDerived, std::move(*extendsType))};
type.derivedTypeSpec().set_scope(*extendsSymbol.scope());
comp.SetType(type);
DerivedTypeDetails &details{symbol.get<DerivedTypeDetails>()};
details.add_component(comp);
@ -4244,15 +4241,15 @@ void DeclarationVisitor::SetType(
}
}
// Find the Symbol for this derived type.
const Symbol *DeclarationVisitor::ResolveDerivedType(const parser::Name &name) {
std::optional<DerivedTypeSpec> DeclarationVisitor::ResolveDerivedType(
const parser::Name &name) {
const Symbol *symbol{FindSymbol(name)};
if (!symbol) {
Say(name, "Derived type '%s' not found"_err_en_US);
return nullptr;
return std::nullopt;
}
if (CheckUseError(name)) {
return nullptr;
return std::nullopt;
}
symbol = &symbol->GetUltimate();
if (auto *details{symbol->detailsIf<GenericDetails>()}) {
@ -4262,9 +4259,22 @@ const Symbol *DeclarationVisitor::ResolveDerivedType(const parser::Name &name) {
}
if (!symbol->has<DerivedTypeDetails>()) {
Say(name, "'%s' is not a derived type"_err_en_US);
return nullptr;
return std::nullopt;
}
return DerivedTypeSpec{name.source, *symbol};
}
std::optional<DerivedTypeSpec> DeclarationVisitor::ResolveExtendsType(
const parser::Name &typeName, const parser::Name *extendsName) {
if (extendsName == nullptr) {
return std::nullopt;
} else if (typeName.source == extendsName->source) {
Say(extendsName->source,
"Derived type '%s' cannot extend itself"_err_en_US);
return std::nullopt;
} else {
return ResolveDerivedType(*extendsName);
}
return symbol;
}
Symbol *DeclarationVisitor::NoteInterfaceName(const parser::Name &name) {

View File

@ -22,14 +22,12 @@
namespace Fortran::semantics {
DerivedTypeSpec::DerivedTypeSpec(const DerivedTypeSpec &that)
: typeSymbol_{that.typeSymbol_}, scope_{that.scope_}, parameters_{
that.parameters_} {}
DerivedTypeSpec::DerivedTypeSpec(DerivedTypeSpec &&that)
: typeSymbol_{that.typeSymbol_}, scope_{that.scope_}, parameters_{std::move(
that.parameters_)} {
DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol)
: name_{name}, typeSymbol_{typeSymbol} {
CHECK(typeSymbol.has<DerivedTypeDetails>());
}
DerivedTypeSpec::DerivedTypeSpec(const DerivedTypeSpec &that) = default;
DerivedTypeSpec::DerivedTypeSpec(DerivedTypeSpec &&that) = default;
void DerivedTypeSpec::set_scope(const Scope &scope) {
CHECK(!scope_);
@ -54,7 +52,7 @@ ParamValue *DerivedTypeSpec::FindParameter(SourceName target) {
std::string DerivedTypeSpec::AsFortran() const {
std::stringstream ss;
ss << typeSymbol_.name().ToString();
ss << name_;
if (!parameters_.empty()) {
ss << '(';
bool first = true;

View File

@ -227,12 +227,15 @@ private:
};
std::ostream &operator<<(std::ostream &, const ArraySpec &);
// Each DerivedTypeSpec has a typeSymbol that has DerivedTypeSpec.
// The name may not match the symbol's name in case of a USE rename.
class DerivedTypeSpec {
public:
explicit DerivedTypeSpec(const Symbol &symbol) : typeSymbol_{symbol} {}
explicit DerivedTypeSpec(SourceName, const Symbol &);
DerivedTypeSpec(const DerivedTypeSpec &);
DerivedTypeSpec(DerivedTypeSpec &&);
const SourceName &name() const { return name_; }
const Symbol &typeSymbol() const { return typeSymbol_; }
const Scope *scope() const { return scope_; }
void set_scope(const Scope &);
@ -258,6 +261,7 @@ public:
std::string AsFortran() const;
private:
SourceName name_;
const Symbol &typeSymbol_;
const Scope *scope_{nullptr}; // same as typeSymbol_.scope() unless PDT
std::map<SourceName, ParamValue> parameters_;

View File

@ -97,10 +97,78 @@ end
! character(l2,4)::x
! interface
! subroutine s(x,y)
! import::l2
! import::f2
! import::l2
! character(l2,4)::x
! character(f2(l2),1)::y
! end
! end interface
!end
module m6a
type t1
end type
end
!Expect: m6a.mod
!module m6a
! type::t1
! end type
!end
module m6b
use m6a, only: t2 => t1
contains
subroutine s(x)
type(t2) :: x
end
end
!Expect: m6b.mod
!module m6b
! use m6a,only:t2=>t1
!contains
! subroutine s(x)
! type(t2)::x
! end
!end
module m6c
use m6a, only: t2 => t1
type, extends(t2) :: t
end type
end
!Expect: m6c.mod
!module m6c
! use m6a,only:t2=>t1
! type,extends(t2)::t
! end type
!end
module m6d
use m6a, only: t2 => t1
type(t2), parameter :: p = t2()
end
!Expect: m6d.mod
!module m6d
! use m6a,only:t2=>t1
! type(t2),parameter::p=t2()
!end
module m6e
use m6a, only: t2 => t1
interface
subroutine s(x)
import t2
type(t2) :: x
end subroutine
end interface
end
!Expect: m6e.mod
!module m6e
! use m6a,only:t2=>t1
! interface
! subroutine s(x)
! import::t2
! type(t2)::x
! end
! end interface
!end

View File

@ -1,4 +1,4 @@
! Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
! 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.
@ -97,7 +97,7 @@ subroutine s1
i = x%t1
!REF: /s1/i
!REF: /s1/x
!DEF: /s1/t3/t2 (ParentComp) ObjectEntity TYPE(t1)
!DEF: /s1/t3/t2 (ParentComp) ObjectEntity TYPE(t2)
!REF: /m1/t1/t1
i = x%t2%t1
end subroutine