forked from OSchip/llvm-project
[flang] Avoid interference between different association variable resoltions
Fix issue 598 and related issues. Transform resolve-names.cc ConstructVisitor association_ member into a stack so that different association construct (select-type, select-rank, change-team, associate) imbrication/succession do not interfere with each other leading to wrong erronous symbol resolution. Original-commit: flang-compiler/f18@5781f29ed6 Reviewed-on: https://github.com/flang-compiler/f18/pull/600 Tree-same-pre-rewrite: false
This commit is contained in:
parent
165b0b8053
commit
8ed0f4c806
|
@ -909,6 +909,8 @@ public:
|
|||
void Post(const parser::EndAssociateStmt &);
|
||||
void Post(const parser::Association &);
|
||||
void Post(const parser::SelectTypeStmt &);
|
||||
bool Pre(const parser::SelectTypeConstruct &);
|
||||
void Post(const parser::SelectTypeConstruct &);
|
||||
bool Pre(const parser::SelectTypeConstruct::TypeCase &);
|
||||
void Post(const parser::SelectTypeConstruct::TypeCase &);
|
||||
void Post(const parser::TypeGuardStmt::Guard &);
|
||||
|
@ -926,6 +928,8 @@ public:
|
|||
bool Pre(const parser::NonLabelDoStmt &x) { return CheckDef(x.t); }
|
||||
bool Pre(const parser::IfThenStmt &x) { return CheckDef(x.t); }
|
||||
bool Pre(const parser::SelectCaseStmt &x) { return CheckDef(x.t); }
|
||||
bool Pre(const parser::SelectRankConstruct &);
|
||||
void Post(const parser::SelectRankConstruct &);
|
||||
bool Pre(const parser::SelectRankStmt &x) {
|
||||
return CheckDef(std::get<0>(x.t));
|
||||
}
|
||||
|
@ -962,10 +966,11 @@ private:
|
|||
MaybeExpr expr;
|
||||
};
|
||||
// association -> [associate-name =>] selector
|
||||
struct {
|
||||
struct Association {
|
||||
const parser::Name *name{nullptr};
|
||||
Selector selector;
|
||||
} association_;
|
||||
};
|
||||
std::vector<Association> associationStack_;
|
||||
|
||||
template<typename T> bool CheckDef(const T &t) {
|
||||
return CheckDef(std::get<std::optional<parser::Name>>(t));
|
||||
|
@ -984,6 +989,9 @@ private:
|
|||
Selector ResolveSelector(const parser::Selector &);
|
||||
void ResolveControlExpressions(const parser::ConcurrentControl &control);
|
||||
void ResolveIndexName(const parser::ConcurrentControl &control);
|
||||
Association &GetCurrentAssociation();
|
||||
void PushAssociation();
|
||||
void PopAssociation();
|
||||
};
|
||||
|
||||
// Walk the parse tree and resolve names to symbols.
|
||||
|
@ -4217,31 +4225,35 @@ bool ConstructVisitor::Pre(const parser::EndBlockStmt &x) {
|
|||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::Selector &x) {
|
||||
association_.selector = ResolveSelector(x);
|
||||
GetCurrentAssociation().selector = ResolveSelector(x);
|
||||
}
|
||||
|
||||
bool ConstructVisitor::Pre(const parser::AssociateStmt &x) {
|
||||
CheckDef(x.t);
|
||||
PushScope(Scope::Kind::Block, nullptr);
|
||||
PushAssociation();
|
||||
return true;
|
||||
}
|
||||
void ConstructVisitor::Post(const parser::EndAssociateStmt &x) {
|
||||
PopAssociation();
|
||||
PopScope();
|
||||
CheckRef(x.v);
|
||||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::Association &x) {
|
||||
const auto &name{std::get<parser::Name>(x.t)};
|
||||
association_.name = &name;
|
||||
GetCurrentAssociation().name = &name;
|
||||
if (auto *symbol{MakeAssocEntity()}) {
|
||||
SetTypeFromAssociation(*symbol);
|
||||
SetAttrsFromAssociation(*symbol);
|
||||
}
|
||||
GetCurrentAssociation() = {}; // clean for further parser::Association.
|
||||
}
|
||||
|
||||
bool ConstructVisitor::Pre(const parser::ChangeTeamStmt &x) {
|
||||
CheckDef(x.t);
|
||||
PushScope(Scope::Kind::Block, nullptr);
|
||||
PushAssociation();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4265,28 +4277,39 @@ void ConstructVisitor::Post(const parser::CoarrayAssociation &x) {
|
|||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::EndChangeTeamStmt &x) {
|
||||
PopAssociation();
|
||||
PopScope();
|
||||
CheckRef(x.t);
|
||||
}
|
||||
|
||||
bool ConstructVisitor::Pre(const parser::SelectTypeConstruct &) {
|
||||
PushAssociation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::SelectTypeConstruct &) {
|
||||
PopAssociation();
|
||||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::SelectTypeStmt &x) {
|
||||
auto &association{GetCurrentAssociation()};
|
||||
if (const std::optional<parser::Name> &name{std::get<1>(x.t)}) {
|
||||
// This isn't a name in the current scope, it is in each TypeGuardStmt
|
||||
MakePlaceholder(*name, MiscDetails::Kind::SelectTypeAssociateName);
|
||||
association_.name = &*name;
|
||||
association.name = &*name;
|
||||
} else {
|
||||
if (const Symbol *
|
||||
whole{UnwrapWholeSymbolDataRef(association_.selector.expr)}) {
|
||||
whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
|
||||
ConvertToObjectEntity(const_cast<Symbol &>(*whole));
|
||||
if (!IsVariableName(*whole)) {
|
||||
Say(association_.selector.source, // C901
|
||||
Say(association.selector.source, // C901
|
||||
"Selector is not a variable"_err_en_US);
|
||||
association_ = {};
|
||||
association = {};
|
||||
}
|
||||
} else {
|
||||
Say(association_.selector.source, // C1157
|
||||
Say(association.selector.source, // C1157
|
||||
"Selector is not a named variable: 'associate-name =>' is required"_err_en_US);
|
||||
association_ = {};
|
||||
association = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4310,6 +4333,15 @@ void ConstructVisitor::Post(const parser::TypeGuardStmt::Guard &x) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ConstructVisitor::Pre(const parser::SelectRankConstruct &) {
|
||||
PushAssociation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConstructVisitor::Post(const parser::SelectRankConstruct &) {
|
||||
PopAssociation();
|
||||
}
|
||||
|
||||
bool ConstructVisitor::CheckDef(const std::optional<parser::Name> &x) {
|
||||
if (x) {
|
||||
MakeSymbol(*x, MiscDetails{MiscDetails::Kind::ConstructName});
|
||||
|
@ -4324,23 +4356,24 @@ void ConstructVisitor::CheckRef(const std::optional<parser::Name> &x) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make a symbol representing an associating entity from association_.
|
||||
// Make a symbol representing an associating entity from current association.
|
||||
Symbol *ConstructVisitor::MakeAssocEntity() {
|
||||
Symbol *symbol{nullptr};
|
||||
if (association_.name) {
|
||||
symbol = &MakeSymbol(*association_.name, UnknownDetails{});
|
||||
auto &association{GetCurrentAssociation()};
|
||||
if (association.name) {
|
||||
symbol = &MakeSymbol(*association.name, UnknownDetails{});
|
||||
if (symbol->has<AssocEntityDetails>() && symbol->owner() == currScope()) {
|
||||
Say(*association_.name, // C1104
|
||||
Say(*association.name, // C1104
|
||||
"The associate name '%s' is already used in this associate statement"_err_en_US);
|
||||
return nullptr;
|
||||
}
|
||||
} else if (const Symbol *
|
||||
whole{UnwrapWholeSymbolDataRef(association_.selector.expr)}) {
|
||||
whole{UnwrapWholeSymbolDataRef(association.selector.expr)}) {
|
||||
symbol = &MakeSymbol(whole->name());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
if (auto &expr{association_.selector.expr}) {
|
||||
if (auto &expr{association.selector.expr}) {
|
||||
symbol->set_details(AssocEntityDetails{common::Clone(*expr)});
|
||||
} else {
|
||||
symbol->set_details(AssocEntityDetails{});
|
||||
|
@ -4353,7 +4386,7 @@ void ConstructVisitor::SetTypeFromAssociation(Symbol &symbol) {
|
|||
auto &details{symbol.get<AssocEntityDetails>()};
|
||||
const MaybeExpr *pexpr{&details.expr()};
|
||||
if (!pexpr->has_value()) {
|
||||
pexpr = &association_.selector.expr;
|
||||
pexpr = &GetCurrentAssociation().selector.expr;
|
||||
}
|
||||
if (pexpr->has_value()) {
|
||||
const SomeExpr &expr{**pexpr};
|
||||
|
@ -4385,7 +4418,7 @@ void ConstructVisitor::SetTypeFromAssociation(Symbol &symbol) {
|
|||
|
||||
// If current selector is a variable, set some of its attributes on symbol.
|
||||
void ConstructVisitor::SetAttrsFromAssociation(Symbol &symbol) {
|
||||
Attrs attrs{evaluate::GetAttrs(association_.selector.expr)};
|
||||
Attrs attrs{evaluate::GetAttrs(GetCurrentAssociation().selector.expr)};
|
||||
symbol.attrs() |= attrs &
|
||||
Attrs{Attr::TARGET, Attr::ASYNCHRONOUS, Attr::VOLATILE, Attr::CONTIGUOUS};
|
||||
if (attrs.test(Attr::POINTER)) {
|
||||
|
@ -4407,6 +4440,20 @@ ConstructVisitor::Selector ConstructVisitor::ResolveSelector(
|
|||
x.u);
|
||||
}
|
||||
|
||||
ConstructVisitor::Association &ConstructVisitor::GetCurrentAssociation() {
|
||||
CHECK(associationStack_.size());
|
||||
return associationStack_.back();
|
||||
}
|
||||
|
||||
void ConstructVisitor::PushAssociation() {
|
||||
associationStack_.emplace_back(Association{});
|
||||
}
|
||||
|
||||
void ConstructVisitor::PopAssociation() {
|
||||
CHECK(associationStack_.size());
|
||||
associationStack_.pop_back();
|
||||
}
|
||||
|
||||
const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec(
|
||||
evaluate::DynamicType &&type) {
|
||||
switch (type.category()) {
|
||||
|
|
|
@ -92,6 +92,7 @@ set(ERROR_TESTS
|
|||
resolve53.f90
|
||||
resolve54.f90
|
||||
resolve55.f90
|
||||
resolve56.f90
|
||||
stop01.f90
|
||||
structconst01.f90
|
||||
structconst02.f90
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
!
|
||||
! Licensed under the Apache License, Version 2.0 (the "License");
|
||||
! you may not use this file except in compliance with the License.
|
||||
! You may obtain a copy of the License at
|
||||
!
|
||||
! http://www.apache.org/licenses/LICENSE-2.0
|
||||
!
|
||||
! Unless required by applicable law or agreed to in writing, software
|
||||
! distributed under the License is distributed on an "AS IS" BASIS,
|
||||
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
! See the License for the specific language governing permissions and
|
||||
! limitations under the License.
|
||||
|
||||
! Test that associations constructs can be correctly combined. The intrinsic
|
||||
! functions are not what is tested here, they are only use to reveal the types
|
||||
! of local variables.
|
||||
|
||||
implicit none
|
||||
real res
|
||||
complex zres
|
||||
integer ires
|
||||
class(*), allocatable :: a, b
|
||||
select type(a)
|
||||
type is (integer)
|
||||
select type(b)
|
||||
type is (integer)
|
||||
ires = selected_int_kind(b)
|
||||
ires = selected_int_kind(a)
|
||||
end select
|
||||
type is (real)
|
||||
res = acos(a)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'CLASS(*)'
|
||||
res = acos(b)
|
||||
end select
|
||||
|
||||
select type(c => a)
|
||||
type is (real)
|
||||
res = acos(c)
|
||||
class default
|
||||
!ERROR: Actual argument for 'x=' has bad type 'CLASS(*)'
|
||||
res = acos(c)
|
||||
end select
|
||||
select type(a)
|
||||
type is (integer)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'Integer(4)'
|
||||
res = acos(a)
|
||||
end select
|
||||
|
||||
select type(b)
|
||||
type is (integer)
|
||||
associate(y=>1.0, x=>1, z=>(1.0,2.3))
|
||||
ires = selected_int_kind(x)
|
||||
select type(a)
|
||||
type is (real)
|
||||
res = acos(a)
|
||||
res = acos(y)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'Integer(4)'
|
||||
res = acos(b)
|
||||
type is (integer)
|
||||
ires = selected_int_kind(b)
|
||||
zres = acos(z)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'Integer(4)'
|
||||
res = acos(a)
|
||||
end select
|
||||
end associate
|
||||
ires = selected_int_kind(b)
|
||||
!ERROR: No explicit type declared for 'c'
|
||||
ires = selected_int_kind(c)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'CLASS(*)'
|
||||
res = acos(a)
|
||||
class default
|
||||
!ERROR: Actual argument for 'r=' has bad type 'CLASS(*)'
|
||||
ires = selected_int_kind(b)
|
||||
end select
|
||||
!ERROR: Actual argument for 'r=' has bad type 'CLASS(*)'
|
||||
ires = selected_int_kind(a)
|
||||
!ERROR: Actual argument for 'x=' has bad type 'CLASS(*)'
|
||||
res = acos(b)
|
||||
end
|
Loading…
Reference in New Issue