775 lines · cpp
1//===-- lib/Evaluate/variable.cpp -----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Evaluate/variable.h"10#include "flang/Common/idioms.h"11#include "flang/Evaluate/check-expression.h"12#include "flang/Evaluate/fold.h"13#include "flang/Evaluate/tools.h"14#include "flang/Parser/char-block.h"15#include "flang/Parser/characters.h"16#include "flang/Parser/message.h"17#include "flang/Semantics/scope.h"18#include "flang/Semantics/symbol.h"19#include <type_traits>20 21using namespace Fortran::parser::literals;22 23namespace Fortran::evaluate {24 25// Constructors, accessors, mutators26 27Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}28 29Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,30 std::optional<Expr<SubscriptInteger>> &&u,31 std::optional<Expr<SubscriptInteger>> &&s)32 : stride_{s ? std::move(*s) : Expr<SubscriptInteger>{1}} {33 if (l) {34 lower_.emplace(std::move(*l));35 }36 if (u) {37 upper_.emplace(std::move(*u));38 }39}40 41std::optional<Expr<SubscriptInteger>> Triplet::lower() const {42 if (lower_) {43 return {lower_.value().value()};44 }45 return std::nullopt;46}47 48Triplet &Triplet::set_lower(Expr<SubscriptInteger> &&expr) {49 lower_.emplace(std::move(expr));50 return *this;51}52 53std::optional<Expr<SubscriptInteger>> Triplet::upper() const {54 if (upper_) {55 return {upper_.value().value()};56 }57 return std::nullopt;58}59 60Triplet &Triplet::set_upper(Expr<SubscriptInteger> &&expr) {61 upper_.emplace(std::move(expr));62 return *this;63}64 65Expr<SubscriptInteger> Triplet::stride() const { return stride_.value(); }66 67Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {68 stride_.value() = std::move(expr);69 return *this;70}71 72CoarrayRef::CoarrayRef(73 DataRef &&base, std::vector<Expr<SubscriptInteger>> &&css)74 : base_{std::move(base)}, cosubscript_(std::move(css)) {}75 76std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {77 if (stat_) {78 return stat_.value().value();79 } else {80 return std::nullopt;81 }82}83 84std::optional<Expr<SomeType>> CoarrayRef::team() const {85 if (team_) {86 return team_.value().value();87 } else {88 return std::nullopt;89 }90}91 92std::optional<Expr<SomeType>> CoarrayRef::notify() const {93 if (notify_) {94 return notify_.value().value();95 } else {96 return std::nullopt;97 }98}99 100CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {101 CHECK(IsVariable(v));102 stat_.emplace(std::move(v));103 return *this;104}105 106CoarrayRef &CoarrayRef::set_team(Expr<SomeType> &&v) {107 team_.emplace(std::move(v));108 return *this;109}110 111CoarrayRef &CoarrayRef::set_notify(Expr<SomeType> &&v) {112 notify_.emplace(std::move(v));113 return *this;114}115 116const Symbol &CoarrayRef::GetFirstSymbol() const {117 return base().GetFirstSymbol();118}119 120const Symbol &CoarrayRef::GetLastSymbol() const {121 return base().GetLastSymbol();122}123 124void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,125 std::optional<Expr<SubscriptInteger>> &upper) {126 if (lower) {127 set_lower(std::move(lower.value()));128 }129 if (upper) {130 set_upper(std::move(upper.value()));131 }132}133 134Expr<SubscriptInteger> Substring::lower() const {135 if (lower_) {136 return lower_.value().value();137 } else {138 return AsExpr(Constant<SubscriptInteger>{1});139 }140}141 142Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {143 lower_.emplace(std::move(expr));144 return *this;145}146 147std::optional<Expr<SubscriptInteger>> Substring::upper() const {148 if (upper_) {149 return upper_.value().value();150 } else {151 return common::visit(152 common::visitors{153 [](const DataRef &dataRef) { return dataRef.LEN(); },154 [](const StaticDataObject::Pointer &object)155 -> std::optional<Expr<SubscriptInteger>> {156 return AsExpr(Constant<SubscriptInteger>{object->data().size()});157 },158 },159 parent_);160 }161}162 163Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {164 upper_.emplace(std::move(expr));165 return *this;166}167 168std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {169 if (!upper_) {170 upper_ = upper();171 if (!upper_) {172 return std::nullopt;173 }174 }175 upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));176 std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())};177 if (!ubi) {178 return std::nullopt;179 }180 if (!lower_) {181 lower_ = AsExpr(Constant<SubscriptInteger>{1});182 }183 lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));184 std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};185 if (!lbi) {186 return std::nullopt;187 }188 if (*lbi > *ubi) { // empty result; canonicalize189 *lbi = 1;190 *ubi = 0;191 lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});192 upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});193 }194 std::optional<ConstantSubscript> length;195 std::optional<Expr<SomeCharacter>> strings; // a Constant<Character>196 if (const auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)}) {197 length = (*literal)->data().size();198 if (auto str{(*literal)->AsString()}) {199 strings =200 Expr<SomeCharacter>(Expr<Ascii>(Constant<Ascii>{std::move(*str)}));201 }202 } else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {203 if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {204 auto folded{evaluate::Fold(context, std::move(*expr))};205 if (IsActuallyConstant(folded)) {206 if (const auto *value{UnwrapExpr<Expr<SomeCharacter>>(folded)}) {207 strings = *value;208 }209 }210 }211 }212 std::optional<Expr<SomeCharacter>> result;213 if (strings) {214 result = common::visit(215 [&](const auto &expr) -> std::optional<Expr<SomeCharacter>> {216 using Type = typename std::decay_t<decltype(expr)>::Result;217 if (const auto *cc{std::get_if<Constant<Type>>(&expr.u)}) {218 if (auto substr{cc->Substring(*lbi, *ubi)}) {219 return Expr<SomeCharacter>{Expr<Type>{*substr}};220 }221 }222 return std::nullopt;223 },224 strings->u);225 }226 if (!result) { // error cases227 if (*lbi < 1) {228 context.Warn(common::UsageWarning::Bounds,229 "Lower bound (%jd) on substring is less than one"_warn_en_US,230 static_cast<std::intmax_t>(*lbi));231 *lbi = 1;232 lower_ = AsExpr(Constant<SubscriptInteger>{1});233 }234 if (length && *ubi > *length) {235 context.Warn(common::UsageWarning::Bounds,236 "Upper bound (%jd) on substring is greater than character length (%jd)"_warn_en_US,237 static_cast<std::intmax_t>(*ubi),238 static_cast<std::intmax_t>(*length));239 *ubi = *length;240 upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});241 }242 }243 return result;244}245 246DescriptorInquiry::DescriptorInquiry(247 const NamedEntity &base, Field field, int dim)248 : base_{base}, field_{field}, dimension_{dim} {249 const Symbol &last{base_.GetLastSymbol()};250 CHECK(IsDescriptor(last));251 CHECK(((field == Field::Len || field == Field::Rank) && dim == 0) ||252 (field != Field::Len && dim >= 0 && dim < last.Rank()));253}254 255DescriptorInquiry::DescriptorInquiry(NamedEntity &&base, Field field, int dim)256 : base_{std::move(base)}, field_{field}, dimension_{dim} {257 const Symbol &last{base_.GetLastSymbol()};258 CHECK(IsDescriptor(last));259 CHECK((field == Field::Len && dim == 0) ||260 (field != Field::Len && dim >= 0 &&261 (dim < last.Rank() || IsAssumedRank(last))));262}263 264// LEN()265static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {266 const Symbol &ultimate{symbol.GetUltimate()};267 if (const auto *assoc{ultimate.detailsIf<semantics::AssocEntityDetails>()}) {268 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc->expr())}) {269 return chExpr->LEN();270 }271 }272 if (auto dyType{DynamicType::From(ultimate)}) {273 auto len{dyType->GetCharLength()};274 if (!len && ultimate.attrs().test(semantics::Attr::PARAMETER)) {275 // Its initializer determines the length of an implied-length named276 // constant.277 if (const auto *object{278 ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {279 if (object->init()) {280 if (auto dyType2{DynamicType::From(*object->init())}) {281 len = dyType2->GetCharLength();282 }283 }284 }285 }286 if (len) {287 if (auto constLen{ToInt64(*len)}) {288 return Expr<SubscriptInteger>{std::max<std::int64_t>(*constLen, 0)};289 } else if (ultimate.owner().IsDerivedType() ||290 IsScopeInvariantExpr(*len)) {291 return AsExpr(Extremum<SubscriptInteger>{292 Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});293 }294 }295 }296 if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {297 return Expr<SubscriptInteger>{298 DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};299 }300 return std::nullopt;301}302 303std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {304 return common::visit(305 common::visitors{306 [](const Symbol &symbol) { return SymbolLEN(symbol); },307 [](const StaticDataObject::Pointer &object)308 -> std::optional<Expr<SubscriptInteger>> {309 return AsExpr(Constant<SubscriptInteger>{object->data().size()});310 },311 },312 u);313}314 315std::optional<Expr<SubscriptInteger>> Component::LEN() const {316 return SymbolLEN(GetLastSymbol());317}318 319std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {320 return SymbolLEN(GetLastSymbol());321}322 323std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {324 return base_.LEN();325}326 327std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {328 return SymbolLEN(GetLastSymbol());329}330 331std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {332 return common::visit(common::visitors{333 [](SymbolRef symbol) { return SymbolLEN(symbol); },334 [](const auto &x) { return x.LEN(); },335 },336 u);337}338 339std::optional<Expr<SubscriptInteger>> Substring::LEN() const {340 if (auto top{upper()}) {341 return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,342 AsExpr(Constant<SubscriptInteger>{0}),343 *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});344 } else {345 return std::nullopt;346 }347}348 349template <typename T>350std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {351 if constexpr (T::category == TypeCategory::Character) {352 return common::visit(common::visitors{353 [](SymbolRef symbol) { return SymbolLEN(symbol); },354 [](const auto &x) { return x.LEN(); },355 },356 u);357 } else {358 common::die("Designator<non-char>::LEN() called");359 return std::nullopt;360 }361}362 363std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {364 using T = std::optional<Expr<SubscriptInteger>>;365 return common::visit(366 common::visitors{367 [](SymbolRef symbol) -> T { return SymbolLEN(symbol); },368 [](const common::CopyableIndirection<Component> &c) -> T {369 return c.value().LEN();370 },371 [](const SpecificIntrinsic &i) -> T {372 // Some cases whose results' lengths can be determined373 // from the lengths of their arguments are handled in374 // ProcedureRef::LEN() before coming here.375 if (const auto &result{i.characteristics.value().functionResult}) {376 if (const auto *type{result->GetTypeAndShape()}) {377 if (auto length{type->type().GetCharLength()}) {378 return std::move(*length);379 }380 }381 }382 return std::nullopt;383 },384 },385 u);386}387 388// Rank()389int BaseObject::Rank() const {390 return common::visit(common::visitors{391 [](SymbolRef symbol) { return symbol->Rank(); },392 [](const StaticDataObject::Pointer &) { return 0; },393 },394 u);395}396 397int Component::Rank() const {398 if (int rank{symbol_->Rank()}; rank > 0) {399 return rank;400 }401 return base().Rank();402}403 404int NamedEntity::Rank() const {405 return common::visit(common::visitors{406 [](const SymbolRef s) { return s->Rank(); },407 [](const Component &c) { return c.Rank(); },408 },409 u_);410}411 412int Subscript::Rank() const {413 return common::visit(common::visitors{414 [](const IndirectSubscriptIntegerExpr &x) {415 return x.value().Rank();416 },417 [](const Triplet &) { return 1; },418 },419 u);420}421 422int ArrayRef::Rank() const {423 int rank{0};424 for (const auto &expr : subscript_) {425 rank += expr.Rank();426 }427 if (rank > 0) {428 return rank;429 } else if (const Component * component{base_.UnwrapComponent()}) {430 return component->base().Rank();431 } else {432 return 0;433 }434}435 436int CoarrayRef::Rank() const { return base().Rank(); }437 438int DataRef::Rank() const {439 return common::visit(common::visitors{440 [](SymbolRef symbol) { return symbol->Rank(); },441 [](const auto &x) { return x.Rank(); },442 },443 u);444}445 446int Substring::Rank() const {447 return common::visit(448 common::visitors{449 [](const DataRef &dataRef) { return dataRef.Rank(); },450 [](const StaticDataObject::Pointer &) { return 0; },451 },452 parent_);453}454 455int ComplexPart::Rank() const { return complex_.Rank(); }456 457template <typename T> int Designator<T>::Rank() const {458 return common::visit(common::visitors{459 [](SymbolRef symbol) { return symbol->Rank(); },460 [](const auto &x) { return x.Rank(); },461 },462 u);463}464 465// Corank()466int BaseObject::Corank() const {467 return common::visit(common::visitors{468 [](SymbolRef symbol) { return symbol->Corank(); },469 [](const StaticDataObject::Pointer &) { return 0; },470 },471 u);472}473 474int Component::Corank() const {475 if (int corank{symbol_->Corank()}; corank > 0) {476 return corank;477 } else if (semantics::IsAllocatableOrObjectPointer(&*symbol_)) {478 return 0; // coarray subobjects ca%a or ca%p are not coarrays479 } else {480 return base().Corank();481 }482}483 484int NamedEntity::Corank() const {485 return common::visit(common::visitors{486 [](const SymbolRef s) { return s->Corank(); },487 [](const Component &c) { return c.Corank(); },488 },489 u_);490}491 492int ArrayRef::Corank() const {493 for (const Subscript &subs : subscript_) {494 if (!std::holds_alternative<Triplet>(subs.u) && subs.Rank() > 0) {495 return 0; // vector-valued subscript - subobject is not a coarray496 }497 }498 return base().Corank();499}500 501int DataRef::Corank() const {502 return common::visit(common::visitors{503 [](SymbolRef symbol) { return symbol->Corank(); },504 [](const auto &x) { return x.Corank(); },505 },506 u);507}508 509int Substring::Corank() const {510 return common::visit(511 common::visitors{512 [](const DataRef &dataRef) { return dataRef.Corank(); },513 [](const StaticDataObject::Pointer &) { return 0; },514 },515 parent_);516}517 518int ComplexPart::Corank() const { return complex_.Corank(); }519 520template <typename T> int Designator<T>::Corank() const {521 return common::visit(common::visitors{522 [](SymbolRef symbol) { return symbol->Corank(); },523 [](const auto &x) { return x.Corank(); },524 },525 u);526}527 528// GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.529const Symbol &Component::GetFirstSymbol() const {530 return base_.value().GetFirstSymbol();531}532 533const Symbol &NamedEntity::GetFirstSymbol() const {534 return common::visit(common::visitors{535 [](SymbolRef s) -> const Symbol & { return s; },536 [](const Component &c) -> const Symbol & {537 return c.GetFirstSymbol();538 },539 },540 u_);541}542 543const Symbol &NamedEntity::GetLastSymbol() const {544 return common::visit(common::visitors{545 [](SymbolRef s) -> const Symbol & { return s; },546 [](const Component &c) -> const Symbol & {547 return c.GetLastSymbol();548 },549 },550 u_);551}552 553const SymbolRef *NamedEntity::UnwrapSymbolRef() const {554 return common::visit(555 common::visitors{556 [](const SymbolRef &s) { return &s; },557 [](const Component &) -> const SymbolRef * { return nullptr; },558 },559 u_);560}561 562SymbolRef *NamedEntity::UnwrapSymbolRef() {563 return common::visit(common::visitors{564 [](SymbolRef &s) { return &s; },565 [](Component &) -> SymbolRef * { return nullptr; },566 },567 u_);568}569 570const Component *NamedEntity::UnwrapComponent() const {571 return common::visit(572 common::visitors{573 [](SymbolRef) -> const Component * { return nullptr; },574 [](const Component &c) { return &c; },575 },576 u_);577}578 579Component *NamedEntity::UnwrapComponent() {580 return common::visit(common::visitors{581 [](SymbolRef &) -> Component * { return nullptr; },582 [](Component &c) { return &c; },583 },584 u_);585}586 587const Symbol &ArrayRef::GetFirstSymbol() const {588 return base_.GetFirstSymbol();589}590 591const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }592 593const Symbol &DataRef::GetFirstSymbol() const {594 return *common::visit(common::visitors{595 [](SymbolRef symbol) { return &*symbol; },596 [](const auto &x) { return &x.GetFirstSymbol(); },597 },598 u);599}600 601const Symbol &DataRef::GetLastSymbol() const {602 return *common::visit(common::visitors{603 [](SymbolRef symbol) { return &*symbol; },604 [](const auto &x) { return &x.GetLastSymbol(); },605 },606 u);607}608 609BaseObject Substring::GetBaseObject() const {610 return common::visit(common::visitors{611 [](const DataRef &dataRef) {612 return BaseObject{dataRef.GetFirstSymbol()};613 },614 [](StaticDataObject::Pointer pointer) {615 return BaseObject{std::move(pointer)};616 },617 },618 parent_);619}620 621const Symbol *Substring::GetLastSymbol() const {622 return common::visit(623 common::visitors{624 [](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },625 [](const auto &) -> const Symbol * { return nullptr; },626 },627 parent_);628}629 630template <typename T> BaseObject Designator<T>::GetBaseObject() const {631 return common::visit(632 common::visitors{633 [](SymbolRef symbol) { return BaseObject{symbol}; },634 [](const Substring &sstring) { return sstring.GetBaseObject(); },635 [](const auto &x) { return BaseObject{x.GetFirstSymbol()}; },636 },637 u);638}639 640template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {641 return common::visit(642 common::visitors{643 [](SymbolRef symbol) { return &*symbol; },644 [](const Substring &sstring) { return sstring.GetLastSymbol(); },645 [](const auto &x) { return &x.GetLastSymbol(); },646 },647 u);648}649 650template <typename T>651std::optional<DynamicType> Designator<T>::GetType() const {652 if constexpr (IsLengthlessIntrinsicType<Result>) {653 return Result::GetType();654 }655 if constexpr (Result::category == TypeCategory::Character) {656 if (std::holds_alternative<Substring>(u)) {657 if (auto len{LEN()}) {658 if (auto n{ToInt64(*len)}) {659 return DynamicType{T::kind, *n};660 }661 }662 return DynamicType{TypeCategory::Character, T::kind};663 }664 }665 if (const Symbol * symbol{GetLastSymbol()}) {666 return DynamicType::From(*symbol);667 }668 return std::nullopt;669}670 671// Equality testing672 673// For the purposes of comparing type parameter expressions while674// testing the compatibility of procedure characteristics, two675// dummy arguments with the same position are considered equal.676static std::optional<int> GetDummyArgPosition(const Symbol &original) {677 const Symbol &symbol(original.GetUltimate());678 if (IsDummy(symbol)) {679 if (const Symbol * proc{symbol.owner().symbol()}) {680 if (const auto *subp{proc->detailsIf<semantics::SubprogramDetails>()}) {681 int j{0};682 for (const Symbol *arg : subp->dummyArgs()) {683 if (arg == &symbol) {684 return j;685 }686 ++j;687 }688 }689 }690 }691 return std::nullopt;692}693 694static bool AreSameSymbol(const Symbol &x, const Symbol &y) {695 if (&x == &y) {696 return true;697 }698 if (auto xPos{GetDummyArgPosition(x)}) {699 if (auto yPos{GetDummyArgPosition(y)}) {700 return *xPos == *yPos;701 }702 }703 return false;704}705 706// Implements operator==() for a union type, using special case handling707// for Symbol references.708template <typename A> static bool TestVariableEquality(const A &x, const A &y) {709 const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};710 if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {711 return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);712 } else {713 return x.u == y.u;714 }715}716 717bool BaseObject::operator==(const BaseObject &that) const {718 return TestVariableEquality(*this, that);719}720bool Component::operator==(const Component &that) const {721 return base_ == that.base_ && &*symbol_ == &*that.symbol_;722}723bool NamedEntity::operator==(const NamedEntity &that) const {724 if (IsSymbol()) {725 return that.IsSymbol() &&726 AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());727 } else {728 return !that.IsSymbol() && GetComponent() == that.GetComponent();729 }730}731bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {732 return &*parameter_ == &*that.parameter_ && base_ == that.base_;733}734bool Triplet::operator==(const Triplet &that) const {735 return lower_ == that.lower_ && upper_ == that.upper_ &&736 stride_ == that.stride_;737}738bool Subscript::operator==(const Subscript &that) const { return u == that.u; }739bool ArrayRef::operator==(const ArrayRef &that) const {740 return base_ == that.base_ && subscript_ == that.subscript_;741}742bool CoarrayRef::operator==(const CoarrayRef &that) const {743 return base_ == that.base_ && cosubscript_ == that.cosubscript_ &&744 stat_ == that.stat_ && team_ == that.team_;745}746bool DataRef::operator==(const DataRef &that) const {747 return TestVariableEquality(*this, that);748}749bool Substring::operator==(const Substring &that) const {750 return parent_ == that.parent_ && lower_ == that.lower_ &&751 upper_ == that.upper_;752}753bool ComplexPart::operator==(const ComplexPart &that) const {754 return part_ == that.part_ && complex_ == that.complex_;755}756bool ProcedureRef::operator==(const ProcedureRef &that) const {757 return proc_ == that.proc_ && arguments_ == that.arguments_;758}759template <typename T>760bool Designator<T>::operator==(const Designator<T> &that) const {761 return TestVariableEquality(*this, that);762}763bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {764 return field_ == that.field_ && base_ == that.base_ &&765 dimension_ == that.dimension_;766}767 768#ifdef _MSC_VER // disable bogus warning about missing definitions769#pragma warning(disable : 4661)770#endif771INSTANTIATE_VARIABLE_TEMPLATES772} // namespace Fortran::evaluate773 774template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;775