Skip to content

Commit

Permalink
[flang][runtime] Initialize uninitialized pointer components
Browse files Browse the repository at this point in the history
Pointer components without default initialization pose some
difficult (or impossible) problems when they appear as right-hand
side targets in pointer assignment statements; they may contain
garbage or stale data that looks enough like a valid descriptor
to cause a crash.  Solve the problem by avoiding it -- ensure
that pointers' descriptors are at least minimally established.

Differential Revision: https://reviews.llvm.org/D149979
  • Loading branch information
klausler committed May 8, 2023
1 parent 5ec6294 commit 27cf6ba
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 25 deletions.
2 changes: 1 addition & 1 deletion flang/include/flang/Semantics/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ bool CanBeTypeBoundProc(const Symbol &);
bool HasDeclarationInitializer(const Symbol &);
// Is the symbol explicitly or implicitly initialized in any way?
bool IsInitialized(const Symbol &, bool ignoreDATAstatements = false,
bool ignoreAllocatable = false);
bool ignoreAllocatable = false, bool ignorePointer = true);
// Is the symbol a component subject to deallocation or finalization?
bool IsDestructible(const Symbol &, const Symbol *derivedType = nullptr);
bool HasIntrinsicTypeName(const Symbol &);
Expand Down
3 changes: 2 additions & 1 deletion flang/include/flang/Semantics/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ class DerivedTypeSpec {

bool MightBeParameterized() const;
bool IsForwardReferenced() const;
bool HasDefaultInitialization(bool ignoreAllocatable = false) const;
bool HasDefaultInitialization(
bool ignoreAllocatable = false, bool ignorePointer = true) const;
bool HasDestruction() const;

// The "raw" type parameter list is a simple transcription from the
Expand Down
3 changes: 2 additions & 1 deletion flang/lib/Semantics/check-data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class DataVarChecker : public evaluate::AllTraverse<DataVarChecker, true> {
: IsFunctionResult(symbol) ? "Function result"
: IsAllocatable(symbol) ? "Allocatable"
: IsInitialized(symbol, true /*ignore DATA*/,
true /*ignore allocatable components*/)
true /*ignore allocatable components*/,
true /*ignore uninitialized pointer components*/)
? "Default-initialized"
: IsProcedure(symbol) && !IsPointer(symbol) ? "Procedure"
// remaining checks don't apply to components
Expand Down
14 changes: 8 additions & 6 deletions flang/lib/Semantics/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2066,13 +2066,15 @@ MaybeExpr ExpressionAnalyzer::Analyze(
if (!symbol.test(Symbol::Flag::ParentComp) &&
unavailable.find(symbol.name()) == unavailable.cend()) {
if (IsAllocatable(symbol)) {
// Set all remaining allocatables to explicit NULL()
// Set all remaining allocatables to explicit NULL().
result.Add(symbol, Expr<SomeType>{NullPointer{}});
} else if (const auto *details{
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
if (details->init()) {
result.Add(symbol, common::Clone(*details->init()));
} else { // C799
} else {
const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()};
if (object && object->init()) {
result.Add(symbol, common::Clone(*object->init()));
} else if (IsPointer(symbol)) {
result.Add(symbol, Expr<SomeType>{NullPointer{}});
} else if (object) { // C799
AttachDeclaration(Say(typeName,
"Structure constructor lacks a value for "
"component '%s'"_err_en_US,
Expand Down
4 changes: 2 additions & 2 deletions flang/lib/Semantics/runtime-type-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,8 +626,8 @@ const Symbol *RuntimeTableBuilder::DescribeType(Scope &dtScope) {
// instances without any initialized components, analyze the type
// and set a flag if there's nothing to do for it at run time.
AddValue(dtValues, derivedTypeSchema_, "noinitializationneeded"s,
IntExpr<1>(
derivedTypeSpec && !derivedTypeSpec->HasDefaultInitialization()));
IntExpr<1>(derivedTypeSpec &&
!derivedTypeSpec->HasDefaultInitialization(false, false)));
// Similarly, a flag to short-circuit destruction when not needed.
AddValue(dtValues, derivedTypeSchema_, "nodestructionneeded"s,
IntExpr<1>(isAbstractType ||
Expand Down
12 changes: 7 additions & 5 deletions flang/lib/Semantics/tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,21 +642,23 @@ bool HasDeclarationInitializer(const Symbol &symbol) {
}
}

bool IsInitialized(
const Symbol &symbol, bool ignoreDataStatements, bool ignoreAllocatable) {
bool IsInitialized(const Symbol &symbol, bool ignoreDataStatements,
bool ignoreAllocatable, bool ignorePointer) {
if (!ignoreAllocatable && IsAllocatable(symbol)) {
return true;
} else if (!ignoreDataStatements && symbol.test(Symbol::Flag::InDataStmt)) {
return true;
} else if (HasDeclarationInitializer(symbol)) {
return true;
} else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
IsPointer(symbol)) {
} else if (IsPointer(symbol)) {
return !ignorePointer;
} else if (IsNamedConstant(symbol) || IsFunctionResult(symbol)) {
return false;
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
if (!object->isDummy() && object->type()) {
if (const auto *derived{object->type()->AsDerived()}) {
return derived->HasDefaultInitialization(ignoreAllocatable);
return derived->HasDefaultInitialization(
ignoreAllocatable, ignorePointer);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions flang/lib/Semantics/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,13 @@ bool DerivedTypeSpec::IsForwardReferenced() const {
return typeSymbol_.get<DerivedTypeDetails>().isForwardReferenced();
}

bool DerivedTypeSpec::HasDefaultInitialization(bool ignoreAllocatable) const {
bool DerivedTypeSpec::HasDefaultInitialization(
bool ignoreAllocatable, bool ignorePointer) const {
DirectComponentIterator components{*this};
return bool{std::find_if(
components.begin(), components.end(), [&](const Symbol &component) {
return IsInitialized(component, true, ignoreAllocatable);
return IsInitialized(component, /*ignoreDataStatements=*/true,
ignoreAllocatable, ignorePointer);
})};
}

Expand Down
10 changes: 10 additions & 0 deletions flang/runtime/derived.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ int Initialize(const Descriptor &instance, const typeInfo::DerivedType &derived,
char *ptr{instance.ZeroBasedIndexedElement<char>(j) + comp.offset()};
std::memcpy(ptr, init, bytes);
}
} else if (comp.genre() == typeInfo::Component::Genre::Pointer) {
// Data pointers without explicit initialization are established
// so that they are valid right-hand side targets of pointer
// assignment statements.
for (std::size_t j{0}; j < elements; ++j) {
Descriptor &ptrDesc{*instance.OffsetElement<Descriptor>(
j * byteStride + comp.offset())};
comp.EstablishDescriptor(ptrDesc, instance, terminator);
ptrDesc.raw().attribute = CFI_attribute_pointer;
}
} else if (comp.genre() == typeInfo::Component::Genre::Data &&
comp.derivedType() && !comp.derivedType()->noInitializationNeeded()) {
// Default initialization of non-pointer non-allocatable/automatic
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/type-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ void Component::EstablishDescriptor(Descriptor &descriptor,
} else {
descriptor.Establish(cat, kind_, nullptr, rank_, nullptr, attribute);
}
if (rank_ && genre_ != Genre::Allocatable) {
if (rank_ && genre_ != Genre::Allocatable && genre_ != Genre::Pointer) {
const typeInfo::Value *boundValues{bounds()};
RUNTIME_CHECK(terminator, boundValues != nullptr);
auto byteStride{static_cast<SubscriptValue>(descriptor.ElementBytes())};
Expand Down
1 change: 0 additions & 1 deletion flang/test/Semantics/canondo01.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

! RUN: %flang_fc1 -fdebug-unparse-with-symbols %s 2>&1 | FileCheck %s
! CHECK: end do

Expand Down
9 changes: 9 additions & 0 deletions flang/test/Semantics/structconst07.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
type :: hasPointer
class(*), pointer :: sp
end type
type(hasPointer) hp
!CHECK: hp=haspointer(sp=NULL())
hp = hasPointer()
end

5 changes: 0 additions & 5 deletions flang/test/Semantics/structconst07.f90#

This file was deleted.

9 changes: 9 additions & 0 deletions flang/test/Semantics/typeinfo03.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
!RUN: bbc --dump-symbols %s | FileCheck %s
!RUN: %flang_fc1 -fdebug-dump-symbols %s | FileCheck %s
!Ensure that type with pointer component(s) has "noinitializationneeded=0"
module m
type hasPointer
class(*), pointer :: sp, ap(:)
end type
end module
!CHECK: .dt.haspointer, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.haspointer,sizeinbytes=104_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=.c.haspointer,procptr=NULL(),special=NULL(),specialbitset=0_4,hasparent=0_1,noinitializationneeded=0_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)

0 comments on commit 27cf6ba

Please sign in to comment.