Index: flang/docs/Extensions.md =================================================================== --- flang/docs/Extensions.md +++ flang/docs/Extensions.md @@ -163,6 +163,10 @@ hold true for definable arguments. * Assignment of `LOGICAL` to `INTEGER` and vice versa (but not other types) is allowed. The values are normalized. +* Static initialization of `LOGICAL` with `INTEGER` is allowed in `DATA` statements + and object initializers. + The results are *not* normalized to canonical `.TRUE.`/`.FALSE.`. + Static initialization of `INTEGER` with `LOGICAL` is also permitted. * An effectively empty source file (no program unit) is accepted and produces an empty relocatable output file. * A `RETURN` statement may appear in a main program. Index: flang/include/flang/Evaluate/logical.h =================================================================== --- flang/include/flang/Evaluate/logical.h +++ flang/include/flang/Evaluate/logical.h @@ -17,6 +17,7 @@ template class Logical { public: static constexpr int bits{BITS}; + using Word = Integer; // Module ISO_C_BINDING kind C_BOOL is LOGICAL(KIND=1) and must have // C's bit representation (.TRUE. -> 1, .FALSE. -> 0). @@ -26,12 +27,19 @@ template constexpr Logical(Logical x) : word_{Represent(x.IsTrue())} {} constexpr Logical(bool truth) : word_{Represent(truth)} {} + // A raw word, for DATA initialization + constexpr Logical(Word &&w) : word_{std::move(w)} {} template constexpr Logical &operator=(Logical x) { word_ = Represent(x.IsTrue()); return *this; } + Word word() const { return word_; } + bool IsCanonical() const { + return word_ == canonicalFalse || word_ == canonicalTrue; + } + // Fortran actually has only .EQV. & .NEQV. relational operations // for LOGICAL, but this template class supports more so that // it can be used with the STL for sorting and as a key type for @@ -86,13 +94,11 @@ } private: - using Word = Integer; - static constexpr Word canonicalTrue{IsLikeC ? -std::uint64_t{1} : 1}; + static constexpr Word canonicalTrue{IsLikeC ? 1 : -std::uint64_t{1}}; static constexpr Word canonicalFalse{0}; static constexpr Word Represent(bool x) { return x ? canonicalTrue : canonicalFalse; } - constexpr Logical(const Word &w) : word_{w} {} Word word_; }; Index: flang/include/flang/Evaluate/tools.h =================================================================== --- flang/include/flang/Evaluate/tools.h +++ flang/include/flang/Evaluate/tools.h @@ -1030,6 +1030,11 @@ } } +// Nonstandard conversions of constants (integer->logical, logical->integer) +// that can appear in DATA statements as an extension. +std::optional> DataConstantConversionExtension( + FoldingContext &, const DynamicType &, const Expr &); + } // namespace Fortran::evaluate namespace Fortran::semantics { Index: flang/include/flang/Semantics/semantics.h =================================================================== --- flang/include/flang/Semantics/semantics.h +++ flang/include/flang/Semantics/semantics.h @@ -75,8 +75,12 @@ return defaultKinds_.doublePrecisionKind(); } int quadPrecisionKind() const { return defaultKinds_.quadPrecisionKind(); } - bool IsEnabled(common::LanguageFeature) const; - bool ShouldWarn(common::LanguageFeature) const; + bool IsEnabled(common::LanguageFeature feature) const { + return languageFeatures_.IsEnabled(feature); + } + bool ShouldWarn(common::LanguageFeature feature) const { + return languageFeatures_.ShouldWarn(feature); + } const std::optional &location() const { return location_; } const std::vector &searchDirectories() const { return searchDirectories_; Index: flang/lib/Evaluate/check-expression.cpp =================================================================== --- flang/lib/Evaluate/check-expression.cpp +++ flang/lib/Evaluate/check-expression.cpp @@ -385,7 +385,7 @@ // Converts, folds, and then checks type, rank, and shape of an // initialization expression for a named constant, a non-pointer -// variable static initializatio, a component default initializer, +// variable static initialization, a component default initializer, // a type parameter default value, or instantiated type parameter value. std::optional> NonPointerInitializationExpr(const Symbol &symbol, Expr &&x, FoldingContext &context, @@ -394,7 +394,20 @@ if (auto symTS{ characteristics::TypeAndShape::Characterize(symbol, context)}) { auto xType{x.GetType()}; - if (auto converted{ConvertToType(symTS->type(), std::move(x))}) { + auto converted{ConvertToType(symTS->type(), Expr{x})}; + if (!converted && + symbol.owner().context().IsEnabled( + common::LanguageFeature::LogicalIntegerAssignment)) { + converted = DataConstantConversionExtension(context, symTS->type(), x); + if (converted && + symbol.owner().context().ShouldWarn( + common::LanguageFeature::LogicalIntegerAssignment)) { + context.messages().Say( + "nonstandard usage: initialization of %s with %s"_en_US, + symTS->type().AsFortran(), x.GetType().value().AsFortran()); + } + } + if (converted) { auto folded{Fold(context, std::move(*converted))}; if (IsActuallyConstant(folded)) { int symRank{GetRank(symTS->shape())}; Index: flang/lib/Evaluate/formatting.cpp =================================================================== --- flang/lib/Evaluate/formatting.cpp +++ flang/lib/Evaluate/formatting.cpp @@ -56,12 +56,14 @@ } else if constexpr (Result::category == TypeCategory::Character) { o << Result::kind << '_' << parser::QuoteCharacterLiteral(value, true); } else if constexpr (Result::category == TypeCategory::Logical) { - if (value.IsTrue()) { - o << ".true."; + if (!value.IsCanonical()) { + o << "transfer(" << value.word().ToInt64() << "_8,.false._" + << Result::kind << ')'; + } else if (value.IsTrue()) { + o << ".true." << '_' << Result::kind; } else { - o << ".false."; + o << ".false." << '_' << Result::kind; } - o << '_' << Result::kind; } else { StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o); } Index: flang/lib/Evaluate/tools.cpp =================================================================== --- flang/lib/Evaluate/tools.cpp +++ flang/lib/Evaluate/tools.cpp @@ -1010,6 +1010,71 @@ return std::visit([](const auto &y) { return GetLastPointerSymbol(y); }, x.u); } +template +static std::optional> DataConstantConversionHelper( + FoldingContext &context, const DynamicType &toType, + const Expr &expr) { + DynamicType sizedType{FROM, toType.kind()}; + if (auto sized{ + Fold(context, ConvertToType(sizedType, Expr{expr}))}) { + if (const auto *someExpr{UnwrapExpr>>(*sized)}) { + return std::visit( + [](const auto &w) -> std::optional> { + using FromType = typename std::decay_t::Result; + static constexpr int kind{FromType::kind}; + if constexpr (IsValidKindOfIntrinsicType(TO, kind)) { + if (const auto *fromConst{UnwrapExpr>(w)}) { + using FromWordType = typename FromType::Scalar; + using LogicalType = value::Logical; + using ElementType = + std::conditional_t; + std::vector values; + auto at{fromConst->lbounds()}; + auto shape{fromConst->shape()}; + for (auto n{GetSize(shape)}; n-- > 0; + fromConst->IncrementSubscripts(at)) { + auto elt{fromConst->At(at)}; + if constexpr (TO == TypeCategory::Logical) { + values.emplace_back(std::move(elt)); + } else { + values.emplace_back(elt.word()); + } + } + return {AsGenericExpr(AsExpr(Constant>{ + std::move(values), std::move(shape)}))}; + } + } + return std::nullopt; + }, + someExpr->u); + } + } + return std::nullopt; +} + +std::optional> DataConstantConversionExtension( + FoldingContext &context, const DynamicType &toType, + const Expr &expr0) { + Expr expr{Fold(context, Expr{expr0})}; + if (!IsActuallyConstant(expr)) { + return std::nullopt; + } + if (auto fromType{expr.GetType()}) { + if (toType.category() == TypeCategory::Logical && + fromType->category() == TypeCategory::Integer) { + return DataConstantConversionHelper(context, toType, expr); + } + if (toType.category() == TypeCategory::Integer && + fromType->category() == TypeCategory::Logical) { + return DataConstantConversionHelper(context, toType, expr); + } + } + return std::nullopt; +} + } // namespace Fortran::evaluate namespace Fortran::semantics { Index: flang/lib/Semantics/data-to-inits.cpp =================================================================== --- flang/lib/Semantics/data-to-inits.cpp +++ flang/lib/Semantics/data-to-inits.cpp @@ -237,8 +237,9 @@ return folder.isEmpty(); } -std::optional> -DataInitializationCompiler::ConvertElement( +template +std::optional> +DataInitializationCompiler::ConvertElement( const SomeExpr &expr, const evaluate::DynamicType &type) { if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) { return {std::make_pair(std::move(*converted), false)}; @@ -262,6 +263,18 @@ return {std::make_pair(std::move(*converted), true)}; } } + SemanticsContext &context{exprAnalyzer_.context()}; + if (context.IsEnabled(common::LanguageFeature::LogicalIntegerAssignment)) { + if (MaybeExpr converted{evaluate::DataConstantConversionExtension( + exprAnalyzer_.GetFoldingContext(), type, expr)}) { + if (context.ShouldWarn( + common::LanguageFeature::LogicalIntegerAssignment)) { + context.Say("nonstandard usage: initialization of %s with %s"_en_US, + type.AsFortran(), expr.GetType().value().AsFortran()); + } + return {std::make_pair(std::move(*converted), false)}; + } + } return std::nullopt; } Index: flang/lib/Semantics/semantics.cpp =================================================================== --- flang/lib/Semantics/semantics.cpp +++ flang/lib/Semantics/semantics.cpp @@ -195,14 +195,6 @@ return defaultKinds_.GetDefaultKind(category); } -bool SemanticsContext::IsEnabled(common::LanguageFeature feature) const { - return languageFeatures_.IsEnabled(feature); -} - -bool SemanticsContext::ShouldWarn(common::LanguageFeature feature) const { - return languageFeatures_.ShouldWarn(feature); -} - const DeclTypeSpec &SemanticsContext::MakeNumericType( TypeCategory category, int kind) { if (kind == 0) { Index: flang/test/Semantics/data06.f90 =================================================================== --- flang/test/Semantics/data06.f90 +++ flang/test/Semantics/data06.f90 @@ -43,8 +43,6 @@ data jx/'abc'/ !ERROR: DATA statement value could not be converted to the type 'INTEGER(4)' of the object 'jx' data jx/t1()/ - !ERROR: DATA statement value could not be converted to the type 'INTEGER(4)' of the object 'jx' - data jx/.false./ !ERROR: DATA statement value 'jy' for 'jx' is not a constant data jx/jy/ end subroutine Index: flang/test/Semantics/data15.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/data15.f90 @@ -0,0 +1,15 @@ +! RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s +! Verify initialization extension: integer with logical, logical with integer +! CHECK: d (InDataStmt) size=20 offset=40: ObjectEntity type: LOGICAL(4) shape: 1_8:5_8 init:[LOGICAL(4)::transfer(-2_8,.false._4),transfer(-1_8,.false._4),.false._4,.true._4,transfer(2_8,.false._4)] +! CHECK: j (InDataStmt) size=8 offset=60: ObjectEntity type: INTEGER(4) shape: 1_8:2_8 init:[INTEGER(4)::0_4,1_4] +! CHECK: x, PARAMETER size=20 offset=0: ObjectEntity type: LOGICAL(4) shape: 1_8:5_8 init:[LOGICAL(4)::transfer(-2_8,.false._4),transfer(-1_8,.false._4),.false._4,.true._4,transfer(2_8,.false._4)] +! CHECK: y, PARAMETER size=20 offset=20: ObjectEntity type: INTEGER(4) shape: 1_8:5_8 init:[INTEGER(4)::-2_4,-1_4,0_4,1_4,2_4] +program main + logical, parameter :: x(5) = [ -2, -1, 0, 1, 2 ] + integer, parameter :: y(5) = x + logical :: d(5) + integer :: j(2) + data d / -2, -1, 0, 1, 2 / + data j / .false., .true. / +end +