2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/pointer-assignment.cpp ------------------------------===//
|
2020-01-07 01:16:18 +08:00
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "pointer-assignment.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Common/idioms.h"
|
|
|
|
#include "flang/Common/restorer.h"
|
|
|
|
#include "flang/Evaluate/characteristics.h"
|
|
|
|
#include "flang/Evaluate/expression.h"
|
|
|
|
#include "flang/Evaluate/fold.h"
|
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Parser/message.h"
|
|
|
|
#include "flang/Parser/parse-tree-visitor.h"
|
|
|
|
#include "flang/Parser/parse-tree.h"
|
|
|
|
#include "flang/Semantics/expression.h"
|
|
|
|
#include "flang/Semantics/symbol.h"
|
|
|
|
#include "flang/Semantics/tools.h"
|
2020-02-28 23:11:03 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2020-01-07 01:16:18 +08:00
|
|
|
#include <optional>
|
|
|
|
#include <set>
|
|
|
|
#include <string>
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
// Semantic checks for pointer assignment.
|
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
|
|
|
using namespace parser::literals;
|
|
|
|
using evaluate::characteristics::DummyDataObject;
|
|
|
|
using evaluate::characteristics::FunctionResult;
|
|
|
|
using evaluate::characteristics::Procedure;
|
|
|
|
using evaluate::characteristics::TypeAndShape;
|
2020-01-22 09:15:21 +08:00
|
|
|
using parser::MessageFixedText;
|
|
|
|
using parser::MessageFormattedText;
|
2020-01-07 01:16:18 +08:00
|
|
|
|
|
|
|
class PointerAssignmentChecker {
|
|
|
|
public:
|
2020-01-22 09:15:21 +08:00
|
|
|
PointerAssignmentChecker(evaluate::FoldingContext &context,
|
|
|
|
parser::CharBlock source, const std::string &description)
|
2020-03-29 12:00:16 +08:00
|
|
|
: context_{context}, source_{source}, description_{description} {}
|
2020-01-22 09:15:21 +08:00
|
|
|
PointerAssignmentChecker(evaluate::FoldingContext &context, const Symbol &lhs)
|
2020-03-29 12:00:16 +08:00
|
|
|
: context_{context}, source_{lhs.name()},
|
2021-06-16 06:17:16 +08:00
|
|
|
description_{"pointer '"s + lhs.name().ToString() + '\''}, lhs_{&lhs} {
|
2020-01-22 09:15:21 +08:00
|
|
|
set_lhsType(TypeAndShape::Characterize(lhs, context));
|
|
|
|
set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS));
|
|
|
|
set_isVolatile(lhs.attrs().test(Attr::VOLATILE));
|
2021-06-16 06:17:16 +08:00
|
|
|
if (IsProcedure(lhs)) {
|
|
|
|
procedure_ = Procedure::Characterize(lhs, context);
|
|
|
|
}
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
2020-01-07 01:16:18 +08:00
|
|
|
PointerAssignmentChecker &set_lhsType(std::optional<TypeAndShape> &&);
|
|
|
|
PointerAssignmentChecker &set_isContiguous(bool);
|
2020-01-14 08:39:00 +08:00
|
|
|
PointerAssignmentChecker &set_isVolatile(bool);
|
2020-01-22 09:15:21 +08:00
|
|
|
PointerAssignmentChecker &set_isBoundsRemapping(bool);
|
2020-06-19 08:17:04 +08:00
|
|
|
bool Check(const SomeExpr &);
|
2020-01-07 01:16:18 +08:00
|
|
|
|
|
|
|
private:
|
2020-06-19 08:17:04 +08:00
|
|
|
template <typename T> bool Check(const T &);
|
|
|
|
template <typename T> bool Check(const evaluate::Expr<T> &);
|
|
|
|
template <typename T> bool Check(const evaluate::FunctionRef<T> &);
|
|
|
|
template <typename T> bool Check(const evaluate::Designator<T> &);
|
|
|
|
bool Check(const evaluate::NullPointer &);
|
|
|
|
bool Check(const evaluate::ProcedureDesignator &);
|
|
|
|
bool Check(const evaluate::ProcedureRef &);
|
2020-01-07 01:16:18 +08:00
|
|
|
// Target is a procedure
|
2020-06-19 08:17:04 +08:00
|
|
|
bool Check(
|
2020-01-07 01:16:18 +08:00
|
|
|
parser::CharBlock rhsName, bool isCall, const Procedure * = nullptr);
|
2020-01-14 08:39:00 +08:00
|
|
|
bool LhsOkForUnlimitedPoly() const;
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename... A> parser::Message *Say(A &&...);
|
2020-01-07 01:16:18 +08:00
|
|
|
|
|
|
|
evaluate::FoldingContext &context_;
|
2020-01-22 09:15:21 +08:00
|
|
|
const parser::CharBlock source_;
|
|
|
|
const std::string description_;
|
2020-01-07 01:16:18 +08:00
|
|
|
const Symbol *lhs_{nullptr};
|
|
|
|
std::optional<TypeAndShape> lhsType_;
|
|
|
|
std::optional<Procedure> procedure_;
|
|
|
|
bool isContiguous_{false};
|
2020-01-14 08:39:00 +08:00
|
|
|
bool isVolatile_{false};
|
2020-01-22 09:15:21 +08:00
|
|
|
bool isBoundsRemapping_{false};
|
2020-01-07 01:16:18 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
PointerAssignmentChecker &PointerAssignmentChecker::set_lhsType(
|
|
|
|
std::optional<TypeAndShape> &&lhsType) {
|
|
|
|
lhsType_ = std::move(lhsType);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
PointerAssignmentChecker &PointerAssignmentChecker::set_isContiguous(
|
|
|
|
bool isContiguous) {
|
|
|
|
isContiguous_ = isContiguous;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-01-14 08:39:00 +08:00
|
|
|
PointerAssignmentChecker &PointerAssignmentChecker::set_isVolatile(
|
|
|
|
bool isVolatile) {
|
|
|
|
isVolatile_ = isVolatile;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-01-22 09:15:21 +08:00
|
|
|
PointerAssignmentChecker &PointerAssignmentChecker::set_isBoundsRemapping(
|
|
|
|
bool isBoundsRemapping) {
|
|
|
|
isBoundsRemapping_ = isBoundsRemapping;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
template <typename T> bool PointerAssignmentChecker::Check(const T &) {
|
2020-01-07 01:16:18 +08:00
|
|
|
// Catch-all case for really bad target expression
|
|
|
|
Say("Target associated with %s must be a designator or a call to a"
|
|
|
|
" pointer-valued function"_err_en_US,
|
|
|
|
description_);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::Expr<T> &x) {
|
|
|
|
return std::visit([&](const auto &x) { return Check(x); }, x.u);
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const SomeExpr &rhs) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (HasVectorSubscript(rhs)) { // C1025
|
2020-01-07 01:16:18 +08:00
|
|
|
Say("An array section with a vector subscript may not be a pointer target"_err_en_US);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (ExtractCoarrayRef(rhs)) { // C1026
|
2020-01-07 01:16:18 +08:00
|
|
|
Say("A coindexed object may not be a pointer target"_err_en_US);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
} else {
|
2020-06-19 08:17:04 +08:00
|
|
|
return std::visit([&](const auto &x) { return Check(x); }, rhs.u);
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::NullPointer &) {
|
|
|
|
return true; // P => NULL() without MOLD=; always OK
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
|
2020-01-07 01:16:18 +08:00
|
|
|
std::string funcName;
|
|
|
|
const auto *symbol{f.proc().GetSymbol()};
|
|
|
|
if (symbol) {
|
|
|
|
funcName = symbol->name().ToString();
|
|
|
|
} else if (const auto *intrinsic{f.proc().GetSpecificIntrinsic()}) {
|
|
|
|
funcName = intrinsic->name;
|
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
auto proc{Procedure::Characterize(f.proc(), context_)};
|
2020-01-07 01:16:18 +08:00
|
|
|
if (!proc) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
2020-01-22 09:15:21 +08:00
|
|
|
std::optional<MessageFixedText> msg;
|
2020-03-29 12:00:16 +08:00
|
|
|
const auto &funcResult{proc->functionResult}; // C1025
|
2020-01-07 01:16:18 +08:00
|
|
|
if (!funcResult) {
|
|
|
|
msg = "%s is associated with the non-existent result of reference to"
|
|
|
|
" procedure"_err_en_US;
|
|
|
|
} else if (procedure_) {
|
|
|
|
// Shouldn't be here in this function unless lhs is an object pointer.
|
|
|
|
msg = "Procedure %s is associated with the result of a reference to"
|
|
|
|
" function '%s' that does not return a procedure pointer"_err_en_US;
|
|
|
|
} else if (funcResult->IsProcedurePointer()) {
|
|
|
|
msg = "Object %s is associated with the result of a reference to"
|
|
|
|
" function '%s' that is a procedure pointer"_err_en_US;
|
|
|
|
} else if (!funcResult->attrs.test(FunctionResult::Attr::Pointer)) {
|
|
|
|
msg = "%s is associated with the result of a reference to function '%s'"
|
|
|
|
" that is a not a pointer"_err_en_US;
|
|
|
|
} else if (isContiguous_ &&
|
|
|
|
!funcResult->attrs.test(FunctionResult::Attr::Contiguous)) {
|
|
|
|
msg = "CONTIGUOUS %s is associated with the result of reference to"
|
|
|
|
" function '%s' that is not contiguous"_err_en_US;
|
|
|
|
} else if (lhsType_) {
|
|
|
|
const auto *frTypeAndShape{funcResult->GetTypeAndShape()};
|
|
|
|
CHECK(frTypeAndShape);
|
2020-12-16 02:54:36 +08:00
|
|
|
if (!lhsType_->IsCompatibleWith(context_.messages(), *frTypeAndShape,
|
|
|
|
"pointer", "function result", false /*elemental*/,
|
2021-06-04 06:48:16 +08:00
|
|
|
evaluate::CheckConformanceFlags::BothDeferredShape)) {
|
2021-09-17 23:19:10 +08:00
|
|
|
return false; // IsCompatibleWith() emitted message
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
auto restorer{common::ScopedSet(lhs_, symbol)};
|
|
|
|
Say(*msg, description_, funcName);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return true;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::Designator<T> &d) {
|
2020-01-07 01:16:18 +08:00
|
|
|
const Symbol *last{d.GetLastSymbol()};
|
|
|
|
const Symbol *base{d.GetBaseObject().symbol()};
|
|
|
|
if (!last || !base) {
|
|
|
|
// P => "character literal"(1:3)
|
|
|
|
context_.messages().Say("Pointer target is not a named entity"_err_en_US);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
2020-01-22 09:15:21 +08:00
|
|
|
std::optional<std::variant<MessageFixedText, MessageFormattedText>> msg;
|
2020-01-07 01:16:18 +08:00
|
|
|
if (procedure_) {
|
|
|
|
// Shouldn't be here in this function unless lhs is an object pointer.
|
|
|
|
msg = "In assignment to procedure %s, the target is not a procedure or"
|
|
|
|
" procedure pointer"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (!evaluate::GetLastTarget(GetSymbolVector(d))) { // C1025
|
2020-01-07 01:16:18 +08:00
|
|
|
msg = "In assignment to object %s, the target '%s' is not an object with"
|
|
|
|
" POINTER or TARGET attributes"_err_en_US;
|
2020-01-16 06:11:48 +08:00
|
|
|
} else if (auto rhsType{TypeAndShape::Characterize(d, context_)}) {
|
2020-01-14 08:39:00 +08:00
|
|
|
if (!lhsType_) {
|
2020-01-07 01:16:18 +08:00
|
|
|
msg = "%s associated with object '%s' with incompatible type or"
|
|
|
|
" shape"_err_en_US;
|
2020-01-14 08:39:00 +08:00
|
|
|
} else if (rhsType->corank() > 0 &&
|
2020-03-29 12:00:16 +08:00
|
|
|
(isVolatile_ != last->attrs().test(Attr::VOLATILE))) { // C1020
|
2020-01-16 06:11:48 +08:00
|
|
|
// TODO: what if A is VOLATILE in A%B%C? need a better test here
|
2020-01-14 08:39:00 +08:00
|
|
|
if (isVolatile_) {
|
|
|
|
msg = "Pointer may not be VOLATILE when target is a"
|
|
|
|
" non-VOLATILE coarray"_err_en_US;
|
|
|
|
} else {
|
|
|
|
msg = "Pointer must be VOLATILE when target is a"
|
|
|
|
" VOLATILE coarray"_err_en_US;
|
|
|
|
}
|
|
|
|
} else if (rhsType->type().IsUnlimitedPolymorphic()) {
|
|
|
|
if (!LhsOkForUnlimitedPoly()) {
|
|
|
|
msg = "Pointer type must be unlimited polymorphic or non-extensible"
|
|
|
|
" derived type when target is unlimited polymorphic"_err_en_US;
|
|
|
|
}
|
|
|
|
} else {
|
2020-10-01 04:34:23 +08:00
|
|
|
if (!lhsType_->type().IsTkCompatibleWith(rhsType->type())) {
|
2020-01-22 09:15:21 +08:00
|
|
|
msg = MessageFormattedText{
|
|
|
|
"Target type %s is not compatible with pointer type %s"_err_en_US,
|
|
|
|
rhsType->type().AsFortran(), lhsType_->type().AsFortran()};
|
|
|
|
|
|
|
|
} else if (!isBoundsRemapping_) {
|
2020-09-01 02:54:48 +08:00
|
|
|
int lhsRank{evaluate::GetRank(lhsType_->shape())};
|
|
|
|
int rhsRank{evaluate::GetRank(rhsType->shape())};
|
2020-01-22 09:15:21 +08:00
|
|
|
if (lhsRank != rhsRank) {
|
|
|
|
msg = MessageFormattedText{
|
|
|
|
"Pointer has rank %d but target has rank %d"_err_en_US, lhsRank,
|
|
|
|
rhsRank};
|
|
|
|
}
|
|
|
|
}
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
auto restorer{common::ScopedSet(lhs_, last)};
|
2020-01-22 09:15:21 +08:00
|
|
|
if (auto *m{std::get_if<MessageFixedText>(&*msg)}) {
|
2020-02-28 23:11:03 +08:00
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream ss{buf};
|
2020-01-22 09:15:21 +08:00
|
|
|
d.AsFortran(ss);
|
|
|
|
Say(*m, description_, ss.str());
|
|
|
|
} else {
|
|
|
|
Say(std::get<MessageFormattedText>(*msg));
|
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return true;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Common handling for procedure pointer right-hand sides
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(
|
2020-01-14 08:39:00 +08:00
|
|
|
parser::CharBlock rhsName, bool isCall, const Procedure *rhsProcedure) {
|
2020-09-26 00:03:17 +08:00
|
|
|
if (std::optional<MessageFixedText> msg{
|
|
|
|
evaluate::CheckProcCompatibility(isCall, procedure_, rhsProcedure)}) {
|
2020-01-14 08:39:00 +08:00
|
|
|
Say(std::move(*msg), description_, rhsName);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return true;
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::ProcedureDesignator &d) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (auto chars{Procedure::Characterize(d, context_)}) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return Check(d.GetName(), false, &*chars);
|
2020-01-07 01:16:18 +08:00
|
|
|
} else {
|
2020-06-19 08:17:04 +08:00
|
|
|
return Check(d.GetName(), false);
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool PointerAssignmentChecker::Check(const evaluate::ProcedureRef &ref) {
|
2020-01-07 01:16:18 +08:00
|
|
|
const Procedure *procedure{nullptr};
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
auto chars{Procedure::Characterize(ref, context_)};
|
2020-01-07 01:16:18 +08:00
|
|
|
if (chars) {
|
|
|
|
procedure = &*chars;
|
|
|
|
if (chars->functionResult) {
|
|
|
|
if (const auto *proc{chars->functionResult->IsProcedurePointer()}) {
|
|
|
|
procedure = proc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return Check(ref.proc().GetName(), true, procedure);
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-01-14 08:39:00 +08:00
|
|
|
// The target can be unlimited polymorphic if the pointer is, or if it is
|
|
|
|
// a non-extensible derived type.
|
|
|
|
bool PointerAssignmentChecker::LhsOkForUnlimitedPoly() const {
|
|
|
|
const auto &type{lhsType_->type()};
|
|
|
|
if (type.category() != TypeCategory::Derived || type.IsAssumedType()) {
|
|
|
|
return false;
|
|
|
|
} else if (type.IsUnlimitedPolymorphic()) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return !IsExtensibleType(&type.GetDerivedTypeSpec());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename... A>
|
2020-08-07 07:56:14 +08:00
|
|
|
parser::Message *PointerAssignmentChecker::Say(A &&...x) {
|
2020-01-07 01:16:18 +08:00
|
|
|
auto *msg{context_.messages().Say(std::forward<A>(x)...)};
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (msg) {
|
|
|
|
if (lhs_) {
|
|
|
|
return evaluate::AttachDeclaration(msg, *lhs_);
|
|
|
|
}
|
|
|
|
if (!source_.empty()) {
|
|
|
|
msg->Attach(source_, "Declaration of %s"_en_US, description_);
|
|
|
|
}
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2020-01-22 09:15:21 +08:00
|
|
|
// Verify that any bounds on the LHS of a pointer assignment are valid.
|
|
|
|
// Return true if it is a bound-remapping so we can perform further checks.
|
|
|
|
static bool CheckPointerBounds(
|
2020-02-19 07:20:28 +08:00
|
|
|
evaluate::FoldingContext &context, const evaluate::Assignment &assignment) {
|
2020-01-22 09:15:21 +08:00
|
|
|
auto &messages{context.messages()};
|
|
|
|
const SomeExpr &lhs{assignment.lhs};
|
|
|
|
const SomeExpr &rhs{assignment.rhs};
|
|
|
|
bool isBoundsRemapping{false};
|
|
|
|
std::size_t numBounds{std::visit(
|
|
|
|
common::visitors{
|
2020-02-19 07:20:28 +08:00
|
|
|
[&](const evaluate::Assignment::BoundsSpec &bounds) {
|
2020-01-22 09:15:21 +08:00
|
|
|
return bounds.size();
|
|
|
|
},
|
2020-02-19 07:20:28 +08:00
|
|
|
[&](const evaluate::Assignment::BoundsRemapping &bounds) {
|
2020-01-22 09:15:21 +08:00
|
|
|
isBoundsRemapping = true;
|
|
|
|
evaluate::ExtentExpr lhsSizeExpr{1};
|
|
|
|
for (const auto &bound : bounds) {
|
|
|
|
lhsSizeExpr = std::move(lhsSizeExpr) *
|
|
|
|
(common::Clone(bound.second) - common::Clone(bound.first) +
|
|
|
|
evaluate::ExtentExpr{1});
|
|
|
|
}
|
|
|
|
if (std::optional<std::int64_t> lhsSize{evaluate::ToInt64(
|
|
|
|
evaluate::Fold(context, std::move(lhsSizeExpr)))}) {
|
|
|
|
if (auto shape{evaluate::GetShape(context, rhs)}) {
|
|
|
|
if (std::optional<std::int64_t> rhsSize{
|
|
|
|
evaluate::ToInt64(evaluate::Fold(
|
|
|
|
context, evaluate::GetSize(std::move(*shape))))}) {
|
|
|
|
if (*lhsSize > *rhsSize) {
|
|
|
|
messages.Say(
|
|
|
|
"Pointer bounds require %d elements but target has"
|
|
|
|
" only %d"_err_en_US,
|
2020-03-29 12:00:16 +08:00
|
|
|
*lhsSize, *rhsSize); // 10.2.2.3(9)
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bounds.size();
|
|
|
|
},
|
2020-02-19 07:20:28 +08:00
|
|
|
[](const auto &) -> std::size_t {
|
|
|
|
DIE("not valid for pointer assignment");
|
|
|
|
},
|
2020-01-22 09:15:21 +08:00
|
|
|
},
|
2020-02-19 07:20:28 +08:00
|
|
|
assignment.u)};
|
2020-01-22 09:15:21 +08:00
|
|
|
if (numBounds > 0) {
|
|
|
|
if (lhs.Rank() != static_cast<int>(numBounds)) {
|
|
|
|
messages.Say("Pointer '%s' has rank %d but the number of bounds specified"
|
|
|
|
" is %d"_err_en_US,
|
2020-03-29 12:00:16 +08:00
|
|
|
lhs.AsFortran(), lhs.Rank(), numBounds); // C1018
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isBoundsRemapping && rhs.Rank() != 1 &&
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
!evaluate::IsSimplyContiguous(rhs, context)) {
|
2020-01-22 09:15:21 +08:00
|
|
|
messages.Say("Pointer bounds remapping target must have rank 1 or be"
|
2020-03-29 12:00:16 +08:00
|
|
|
" simply contiguous"_err_en_US); // 10.2.2.3(9)
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
|
|
|
return isBoundsRemapping;
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool CheckPointerAssignment(
|
2020-02-19 07:20:28 +08:00
|
|
|
evaluate::FoldingContext &context, const evaluate::Assignment &assignment) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return CheckPointerAssignment(context, assignment.lhs, assignment.rhs,
|
|
|
|
CheckPointerBounds(context, assignment));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckPointerAssignment(evaluate::FoldingContext &context,
|
|
|
|
const SomeExpr &lhs, const SomeExpr &rhs, bool isBoundsRemapping) {
|
2020-01-22 09:15:21 +08:00
|
|
|
const Symbol *pointer{GetLastSymbol(lhs)};
|
|
|
|
if (!pointer) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return false; // error was reported
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
2021-11-05 01:50:20 +08:00
|
|
|
if (!IsPointer(pointer->GetUltimate())) {
|
2020-01-22 09:15:21 +08:00
|
|
|
evaluate::SayWithDeclaration(context.messages(), *pointer,
|
|
|
|
"'%s' is not a pointer"_err_en_US, pointer->name());
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
|
|
|
if (pointer->has<ProcEntityDetails>() && evaluate::ExtractCoarrayRef(lhs)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context.messages().Say( // C1027
|
2020-01-22 09:15:21 +08:00
|
|
|
"Procedure pointer may not be a coindexed object"_err_en_US);
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-01-22 09:15:21 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return PointerAssignmentChecker{context, *pointer}
|
2020-01-22 09:15:21 +08:00
|
|
|
.set_isBoundsRemapping(isBoundsRemapping)
|
|
|
|
.Check(rhs);
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool CheckPointerAssignment(
|
2020-01-07 01:16:18 +08:00
|
|
|
evaluate::FoldingContext &context, const Symbol &lhs, const SomeExpr &rhs) {
|
2020-01-17 04:43:48 +08:00
|
|
|
CHECK(IsPointer(lhs));
|
2020-06-19 08:17:04 +08:00
|
|
|
return PointerAssignmentChecker{context, lhs}.Check(rhs);
|
2020-01-07 01:16:18 +08:00
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool CheckPointerAssignment(evaluate::FoldingContext &context,
|
2020-01-07 01:16:18 +08:00
|
|
|
parser::CharBlock source, const std::string &description,
|
|
|
|
const DummyDataObject &lhs, const SomeExpr &rhs) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return PointerAssignmentChecker{context, source, description}
|
2020-01-07 01:16:18 +08:00
|
|
|
.set_lhsType(common::Clone(lhs.type))
|
|
|
|
.set_isContiguous(lhs.attrs.test(DummyDataObject::Attr::Contiguous))
|
2020-01-14 08:39:00 +08:00
|
|
|
.set_isVolatile(lhs.attrs.test(DummyDataObject::Attr::Volatile))
|
2020-01-07 01:16:18 +08:00
|
|
|
.Check(rhs);
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool CheckInitialTarget(evaluate::FoldingContext &context,
|
|
|
|
const SomeExpr &pointer, const SomeExpr &init) {
|
|
|
|
return evaluate::IsInitialDataTarget(init, &context.messages()) &&
|
|
|
|
CheckPointerAssignment(context, pointer, init);
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::semantics
|