diff options
Diffstat (limited to 'clang/lib/AST')
99 files changed, 9415 insertions, 2549 deletions
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index a22031142c7c..5b0a5e256e41 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -156,10 +156,10 @@ void APValue::LValuePathEntry::Profile(llvm::FoldingSetNodeID &ID) const { APValue::LValuePathSerializationHelper::LValuePathSerializationHelper( ArrayRef<LValuePathEntry> Path, QualType ElemTy) - : ElemTy((const void *)ElemTy.getTypePtrOrNull()), Path(Path) {} + : Ty((const void *)ElemTy.getTypePtrOrNull()), Path(Path) {} QualType APValue::LValuePathSerializationHelper::getType() { - return QualType::getFromOpaquePtr(ElemTy); + return QualType::getFromOpaquePtr(Ty); } namespace { @@ -637,10 +637,10 @@ static bool TryPrintAsStringLiteral(raw_ostream &Out, return false; // Nothing we can do about a sequence that is not null-terminated - if (!Inits.back().getInt().isZero()) + if (!Inits.back().isInt() || !Inits.back().getInt().isZero()) return false; - else - Inits = Inits.drop_back(); + + Inits = Inits.drop_back(); llvm::SmallString<40> Buf; Buf.push_back('"'); @@ -655,6 +655,8 @@ static bool TryPrintAsStringLiteral(raw_ostream &Out, } for (auto &Val : Inits) { + if (!Val.isInt()) + return false; int64_t Char64 = Val.getInt().getExtValue(); if (!isASCII(Char64)) return false; // Bye bye, see you in integers. @@ -871,7 +873,7 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << "...}"; return; } - LLVM_FALLTHROUGH; + [[fallthrough]]; default: getArrayInitializedElt(I).printPretty(Out, Policy, ElemTy, Ctx); } @@ -982,7 +984,7 @@ bool APValue::hasLValuePath() const { ArrayRef<APValue::LValuePathEntry> APValue::getLValuePath() const { assert(isLValue() && hasLValuePath() && "Invalid accessor"); const LV &LVal = *((const LV *)(const char *)&Data); - return llvm::makeArrayRef(LVal.getPath(), LVal.PathLength); + return llvm::ArrayRef(LVal.getPath(), LVal.PathLength); } unsigned APValue::getLValueCallIndex() const { @@ -1060,7 +1062,7 @@ ArrayRef<const CXXRecordDecl*> APValue::getMemberPointerPath() const { assert(isMemberPointer() && "Invalid accessor"); const MemberPointerData &MPD = *((const MemberPointerData *)(const char *)&Data); - return llvm::makeArrayRef(MPD.getPath(), MPD.PathLength); + return llvm::ArrayRef(MPD.getPath(), MPD.PathLength); } void APValue::MakeLValue() { diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index 18582782888c..8f2d94572184 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -19,33 +19,49 @@ #include "llvm/ADT/FoldingSet.h" using namespace clang; -ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, - const ConstraintSatisfaction &Satisfaction): - NumRecords{Satisfaction.Details.size()}, - IsSatisfied{Satisfaction.IsSatisfied} { - for (unsigned I = 0; I < NumRecords; ++I) { - auto &Detail = Satisfaction.Details[I]; - if (Detail.second.is<Expr *>()) - new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I) - UnsatisfiedConstraintRecord{Detail.first, - UnsatisfiedConstraintRecord::second_type( - Detail.second.get<Expr *>())}; - else { - auto &SubstitutionDiagnostic = - *Detail.second.get<std::pair<SourceLocation, StringRef> *>(); - unsigned MessageSize = SubstitutionDiagnostic.second.size(); - char *Mem = new (C) char[MessageSize]; - memcpy(Mem, SubstitutionDiagnostic.second.data(), MessageSize); - auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>( - SubstitutionDiagnostic.first, StringRef(Mem, MessageSize)); - new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I) - UnsatisfiedConstraintRecord{Detail.first, - UnsatisfiedConstraintRecord::second_type( - NewSubstDiag)}; - } +namespace { +void CreatUnsatisfiedConstraintRecord( + const ASTContext &C, const UnsatisfiedConstraintRecord &Detail, + UnsatisfiedConstraintRecord *TrailingObject) { + if (Detail.second.is<Expr *>()) + new (TrailingObject) UnsatisfiedConstraintRecord{ + Detail.first, + UnsatisfiedConstraintRecord::second_type(Detail.second.get<Expr *>())}; + else { + auto &SubstitutionDiagnostic = + *Detail.second.get<std::pair<SourceLocation, StringRef> *>(); + unsigned MessageSize = SubstitutionDiagnostic.second.size(); + char *Mem = new (C) char[MessageSize]; + memcpy(Mem, SubstitutionDiagnostic.second.data(), MessageSize); + auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>( + SubstitutionDiagnostic.first, StringRef(Mem, MessageSize)); + new (TrailingObject) UnsatisfiedConstraintRecord{ + Detail.first, UnsatisfiedConstraintRecord::second_type(NewSubstDiag)}; } } +} // namespace +ASTConstraintSatisfaction::ASTConstraintSatisfaction( + const ASTContext &C, const ConstraintSatisfaction &Satisfaction) + : NumRecords{Satisfaction.Details.size()}, + IsSatisfied{Satisfaction.IsSatisfied}, ContainsErrors{ + Satisfaction.ContainsErrors} { + for (unsigned I = 0; I < NumRecords; ++I) + CreatUnsatisfiedConstraintRecord( + C, Satisfaction.Details[I], + getTrailingObjects<UnsatisfiedConstraintRecord>() + I); +} + +ASTConstraintSatisfaction::ASTConstraintSatisfaction( + const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction) + : NumRecords{Satisfaction.NumRecords}, + IsSatisfied{Satisfaction.IsSatisfied}, + ContainsErrors{Satisfaction.ContainsErrors} { + for (unsigned I = 0; I < NumRecords; ++I) + CreatUnsatisfiedConstraintRecord( + C, *(Satisfaction.begin() + I), + getTrailingObjects<UnsatisfiedConstraintRecord>() + I); +} ASTConstraintSatisfaction * ASTConstraintSatisfaction::Create(const ASTContext &C, @@ -57,6 +73,14 @@ ASTConstraintSatisfaction::Create(const ASTContext &C, return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); } +ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild( + const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction) { + std::size_t size = + totalSizeToAlloc<UnsatisfiedConstraintRecord>(Satisfaction.NumRecords); + void *Mem = C.Allocate(size, alignof(ASTConstraintSatisfaction)); + return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); +} + void ConstraintSatisfaction::Profile( llvm::FoldingSetNodeID &ID, const ASTContext &C, const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cfd7bf604542..2884fe660422 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -71,8 +71,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -94,6 +92,7 @@ #include <cstdlib> #include <map> #include <memory> +#include <optional> #include <string> #include <tuple> #include <utility> @@ -298,6 +297,9 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { return nullptr; const FileID File = SourceMgr.getDecomposedLoc(DeclLoc).first; + if (!File.isValid()) { + return nullptr; + } const auto CommentsInThisFile = Comments.getCommentsInFile(File); if (!CommentsInThisFile || CommentsInThisFile->empty()) return nullptr; @@ -693,7 +695,11 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { ID.AddInteger(1); ID.AddBoolean(NTTP->isParameterPack()); + const Expr *TC = NTTP->getPlaceholderTypeConstraint(); + ID.AddBoolean(TC != nullptr); ID.AddPointer(NTTP->getType().getCanonicalType().getAsOpaquePtr()); + if (TC) + TC->Profile(ID, C, /*Canonical=*/true); if (NTTP->isExpandedParameterPack()) { ID.AddBoolean(true); ID.AddInteger(NTTP->getNumExpansionTypes()); @@ -754,15 +760,19 @@ canonicalizeImmediatelyDeclaredConstraint(const ASTContext &C, Expr *IDC, NewConverted.push_back(ConstrainedType); llvm::append_range(NewConverted, OldConverted.drop_front(1)); } + auto *CSD = ImplicitConceptSpecializationDecl::Create( + C, CSE->getNamedConcept()->getDeclContext(), + CSE->getNamedConcept()->getLocation(), NewConverted); + Expr *NewIDC = ConceptSpecializationExpr::Create( - C, CSE->getNamedConcept(), NewConverted, nullptr, - CSE->isInstantiationDependent(), CSE->containsUnexpandedParameterPack()); + C, CSE->getNamedConcept(), CSD, nullptr, CSE->isInstantiationDependent(), + CSE->containsUnexpandedParameterPack()); if (auto *OrigFold = dyn_cast<CXXFoldExpr>(IDC)) NewIDC = new (C) CXXFoldExpr( - OrigFold->getType(), /*Callee*/nullptr, SourceLocation(), NewIDC, + OrigFold->getType(), /*Callee*/ nullptr, SourceLocation(), NewIDC, BinaryOperatorKind::BO_LAnd, SourceLocation(), /*RHS=*/nullptr, - SourceLocation(), /*NumExpansions=*/None); + SourceLocation(), /*NumExpansions=*/std::nullopt); return NewIDC; } @@ -786,23 +796,18 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( PEnd = Params->end(); P != PEnd; ++P) { if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) { - TemplateTypeParmDecl *NewTTP = TemplateTypeParmDecl::Create(*this, - getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + TemplateTypeParmDecl *NewTTP = TemplateTypeParmDecl::Create( + *this, getTranslationUnitDecl(), SourceLocation(), SourceLocation(), TTP->getDepth(), TTP->getIndex(), nullptr, false, TTP->isParameterPack(), TTP->hasTypeConstraint(), - TTP->isExpandedParameterPack() ? - llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None); + TTP->isExpandedParameterPack() + ? std::optional<unsigned>(TTP->getNumExpansionParameters()) + : std::nullopt); if (const auto *TC = TTP->getTypeConstraint()) { QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0); Expr *NewIDC = canonicalizeImmediatelyDeclaredConstraint( *this, TC->getImmediatelyDeclaredConstraint(), ParamAsArgument); - TemplateArgumentListInfo CanonArgsAsWritten; - if (auto *Args = TC->getTemplateArgsAsWritten()) - for (const auto &ArgLoc : Args->arguments()) - CanonArgsAsWritten.addArgument( - TemplateArgumentLoc(ArgLoc.getArgument(), - TemplateArgumentLocInfo())); NewTTP->setTypeConstraint( NestedNameSpecifierLoc(), DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), @@ -924,38 +929,6 @@ ParentMapContext &ASTContext::getParentMapContext() { return *ParentMapCtx.get(); } -static const LangASMap *getAddressSpaceMap(const TargetInfo &T, - const LangOptions &LOpts) { - if (LOpts.FakeAddressSpaceMap) { - // The fake address space map must have a distinct entry for each - // language-specific address space. - static const unsigned FakeAddrSpaceMap[] = { - 0, // Default - 1, // opencl_global - 3, // opencl_local - 2, // opencl_constant - 0, // opencl_private - 4, // opencl_generic - 5, // opencl_global_device - 6, // opencl_global_host - 7, // cuda_device - 8, // cuda_constant - 9, // cuda_shared - 1, // sycl_global - 5, // sycl_global_device - 6, // sycl_global_host - 3, // sycl_local - 0, // sycl_private - 10, // ptr32_sptr - 11, // ptr32_uptr - 12 // ptr64 - }; - return &FakeAddrSpaceMap; - } else { - return &T.getAddressSpaceMap(); - } -} - static bool isAddrSpaceMapManglingEnabled(const TargetInfo &TI, const LangOptions &LangOpts) { switch (LangOpts.getAddressSpaceMapMangling()) { @@ -1133,7 +1106,7 @@ ASTContext::getModulesWithMergedDefinition(const NamedDecl *Def) { auto MergedIt = MergedDefModules.find(cast<NamedDecl>(Def->getCanonicalDecl())); if (MergedIt == MergedDefModules.end()) - return None; + return std::nullopt; return MergedIt->second; } @@ -1191,7 +1164,7 @@ void ASTContext::addLazyModuleInitializers(Module *M, ArrayRef<uint32_t> IDs) { ArrayRef<Decl *> ASTContext::getModuleInitializers(Module *M) { auto It = ModuleInitializers.find(M); if (It == ModuleInitializers.end()) - return None; + return std::nullopt; auto *Inits = It->second; Inits->resolve(*this); @@ -1286,7 +1259,6 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, this->AuxTarget = AuxTarget; ABI.reset(createCXXABI(Target)); - AddrSpaceMap = getAddressSpaceMap(Target, LangOpts); AddrSpaceMapMangling = isAddrSpaceMapManglingEnabled(Target, LangOpts); // C99 6.2.5p19. @@ -1873,7 +1845,7 @@ static getConstantArrayInfoInChars(const ASTContext &Context, uint64_t Width = EltInfo.Width.getQuantity() * Size; unsigned Align = EltInfo.Align.getQuantity(); if (!Context.getTargetInfo().getCXXABI().isMicrosoft() || - Context.getTargetInfo().getPointerWidth(0) == 64) + Context.getTargetInfo().getPointerWidth(LangAS::Default) == 64) Width = llvm::alignTo(Width, Align); return TypeInfoChars(CharUnits::fromQuantity(Width), CharUnits::fromQuantity(Align), @@ -1892,6 +1864,44 @@ TypeInfoChars ASTContext::getTypeInfoInChars(QualType T) const { return getTypeInfoInChars(T.getTypePtr()); } +bool ASTContext::isPromotableIntegerType(QualType T) const { + // HLSL doesn't promote all small integer types to int, it + // just uses the rank-based promotion rules for all types. + if (getLangOpts().HLSL) + return false; + + if (const auto *BT = T->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Bool: + case BuiltinType::Char_S: + case BuiltinType::Char_U: + case BuiltinType::SChar: + case BuiltinType::UChar: + case BuiltinType::Short: + case BuiltinType::UShort: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + case BuiltinType::Char8: + case BuiltinType::Char16: + case BuiltinType::Char32: + return true; + default: + return false; + } + + // Enumerated types are promotable to their compatible integer types + // (C99 6.3.1.1) a.k.a. its underlying type (C++ [conv.prom]p2). + if (const auto *ET = T->getAs<EnumType>()) { + if (T->isDependentType() || ET->getDecl()->getPromotionType().isNull() || + ET->getDecl()->isScoped()) + return false; + + return true; + } + + return false; +} + bool ASTContext::isAlignmentRequired(const Type *T) const { return getTypeInfo(T).AlignRequirement != AlignRequirementKind::None; } @@ -1946,7 +1956,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { uint64_t Width = 0; unsigned Align = 8; AlignRequirementKind AlignRequirement = AlignRequirementKind::None; - unsigned AS = 0; + LangAS AS = LangAS::Default; switch (T->getTypeClass()) { #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) @@ -1981,7 +1991,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Align = EltInfo.Align; AlignRequirement = EltInfo.AlignRequirement; if (!getTargetInfo().getCXXABI().isMicrosoft() || - getTargetInfo().getPointerWidth(0) == 64) + getTargetInfo().getPointerWidth(LangAS::Default) == 64) Width = llvm::alignTo(Width, Align); break; } @@ -2082,7 +2092,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case BuiltinType::Int128: case BuiltinType::UInt128: Width = 128; - Align = 128; // int128_t is 128-bit aligned on all targets. + Align = Target->getInt128Align(); break; case BuiltinType::ShortAccum: case BuiltinType::UShortAccum: @@ -2181,14 +2191,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { } break; case BuiltinType::NullPtr: - Width = Target->getPointerWidth(0); // C++ 3.9.1p11: sizeof(nullptr_t) - Align = Target->getPointerAlign(0); // == sizeof(void*) + // C++ 3.9.1p11: sizeof(nullptr_t) == sizeof(void*) + Width = Target->getPointerWidth(LangAS::Default); + Align = Target->getPointerAlign(LangAS::Default); break; case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: - Width = Target->getPointerWidth(0); - Align = Target->getPointerAlign(0); + Width = Target->getPointerWidth(LangAS::Default); + Align = Target->getPointerAlign(LangAS::Default); break; case BuiltinType::OCLSampler: case BuiltinType::OCLEvent: @@ -2201,8 +2212,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ case BuiltinType::Id: #include "clang/Basic/OpenCLExtensionTypes.def" - AS = getTargetAddressSpace( - Target->getOpenCLTypeAddrSpace(getOpenCLTypeKind(T))); + AS = Target->getOpenCLTypeAddrSpace(getOpenCLTypeKind(T)); Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; @@ -2247,11 +2257,11 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { } break; case Type::ObjCObjectPointer: - Width = Target->getPointerWidth(0); - Align = Target->getPointerAlign(0); + Width = Target->getPointerWidth(LangAS::Default); + Align = Target->getPointerAlign(LangAS::Default); break; case Type::BlockPointer: - AS = getTargetAddressSpace(cast<BlockPointerType>(T)->getPointeeType()); + AS = cast<BlockPointerType>(T)->getPointeeType().getAddressSpace(); Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; @@ -2259,12 +2269,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::RValueReference: // alignof and sizeof should never enter this code path here, so we go // the pointer route. - AS = getTargetAddressSpace(cast<ReferenceType>(T)->getPointeeType()); + AS = cast<ReferenceType>(T)->getPointeeType().getAddressSpace(); Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; case Type::Pointer: - AS = getTargetAddressSpace(cast<PointerType>(T)->getPointeeType()); + AS = cast<PointerType>(T)->getPointeeType().getAddressSpace(); Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; @@ -2367,12 +2377,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { return getTypeInfo(cast<UsingType>(T)->desugar().getTypePtr()); case Type::Typedef: { - const TypedefNameDecl *Typedef = cast<TypedefType>(T)->getDecl(); - TypeInfo Info = getTypeInfo(Typedef->getUnderlyingType().getTypePtr()); + const auto *TT = cast<TypedefType>(T); + TypeInfo Info = getTypeInfo(TT->desugar().getTypePtr()); // If the typedef has an aligned attribute on it, it overrides any computed // alignment we have. This violates the GCC documentation (which says that // attribute(aligned) can only round up) but matches its implementation. - if (unsigned AttrAlign = Typedef->getMaxAlignment()) { + if (unsigned AttrAlign = TT->getDecl()->getMaxAlignment()) { Align = AttrAlign; AlignRequirement = AlignRequirementKind::RequiredByTypedef; } else { @@ -2421,8 +2431,8 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { break; case Type::Pipe: - Width = Target->getPointerWidth(getTargetAddressSpace(LangAS::opencl_global)); - Align = Target->getPointerAlign(getTargetAddressSpace(LangAS::opencl_global)); + Width = Target->getPointerWidth(LangAS::opencl_global); + Align = Target->getPointerAlign(LangAS::opencl_global); break; } @@ -2485,7 +2495,7 @@ CharUnits ASTContext::getTypeAlignInChars(const Type *T) const { } /// getTypeUnadjustedAlignInChars - Return the ABI-specified alignment of a -/// type, in characters, before alignment adustments. This method does +/// type, in characters, before alignment adjustments. This method does /// not work on incomplete types. CharUnits ASTContext::getTypeUnadjustedAlignInChars(QualType T) const { return toCharUnitsFromBits(getTypeUnadjustedAlign(T)); @@ -2682,11 +2692,11 @@ static int64_t getSubobjectOffset(const CXXRecordDecl *RD, return Context.toBits(Layout.getBaseClassOffset(RD)); } -static llvm::Optional<int64_t> +static std::optional<int64_t> structHasUniqueObjectRepresentations(const ASTContext &Context, const RecordDecl *RD); -static llvm::Optional<int64_t> +static std::optional<int64_t> getSubobjectSizeInBits(const FieldDecl *Field, const ASTContext &Context) { if (Field->getType()->isRecordType()) { const RecordDecl *RD = Field->getType()->getAsRecordDecl(); @@ -2699,7 +2709,7 @@ getSubobjectSizeInBits(const FieldDecl *Field, const ASTContext &Context) { bool IsBitIntType = Field->getType()->isBitIntType(); if (!Field->getType()->isReferenceType() && !IsBitIntType && !Context.hasUniqueObjectRepresentations(Field->getType())) - return llvm::None; + return std::nullopt; int64_t FieldSizeInBits = Context.toBits(Context.getTypeSizeInChars(Field->getType())); @@ -2708,43 +2718,43 @@ getSubobjectSizeInBits(const FieldDecl *Field, const ASTContext &Context) { if (IsBitIntType) { if ((unsigned)BitfieldSize > cast<BitIntType>(Field->getType())->getNumBits()) - return llvm::None; + return std::nullopt; } else if (BitfieldSize > FieldSizeInBits) { - return llvm::None; + return std::nullopt; } FieldSizeInBits = BitfieldSize; } else if (IsBitIntType && !Context.hasUniqueObjectRepresentations(Field->getType())) { - return llvm::None; + return std::nullopt; } return FieldSizeInBits; } -static llvm::Optional<int64_t> +static std::optional<int64_t> getSubobjectSizeInBits(const CXXRecordDecl *RD, const ASTContext &Context) { return structHasUniqueObjectRepresentations(Context, RD); } template <typename RangeT> -static llvm::Optional<int64_t> structSubobjectsHaveUniqueObjectRepresentations( +static std::optional<int64_t> structSubobjectsHaveUniqueObjectRepresentations( const RangeT &Subobjects, int64_t CurOffsetInBits, const ASTContext &Context, const clang::ASTRecordLayout &Layout) { for (const auto *Subobject : Subobjects) { - llvm::Optional<int64_t> SizeInBits = + std::optional<int64_t> SizeInBits = getSubobjectSizeInBits(Subobject, Context); if (!SizeInBits) - return llvm::None; + return std::nullopt; if (*SizeInBits != 0) { int64_t Offset = getSubobjectOffset(Subobject, Context, Layout); if (Offset != CurOffsetInBits) - return llvm::None; + return std::nullopt; CurOffsetInBits += *SizeInBits; } } return CurOffsetInBits; } -static llvm::Optional<int64_t> +static std::optional<int64_t> structHasUniqueObjectRepresentations(const ASTContext &Context, const RecordDecl *RD) { assert(!RD->isUnion() && "Must be struct/class type"); @@ -2753,7 +2763,7 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, int64_t CurOffsetInBits = 0; if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) { if (ClassDecl->isDynamicClass()) - return llvm::None; + return std::nullopt; SmallVector<CXXRecordDecl *, 4> Bases; for (const auto &Base : ClassDecl->bases()) { @@ -2766,19 +2776,19 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, return Layout.getBaseClassOffset(L) < Layout.getBaseClassOffset(R); }); - llvm::Optional<int64_t> OffsetAfterBases = + std::optional<int64_t> OffsetAfterBases = structSubobjectsHaveUniqueObjectRepresentations(Bases, CurOffsetInBits, Context, Layout); if (!OffsetAfterBases) - return llvm::None; + return std::nullopt; CurOffsetInBits = *OffsetAfterBases; } - llvm::Optional<int64_t> OffsetAfterFields = + std::optional<int64_t> OffsetAfterFields = structSubobjectsHaveUniqueObjectRepresentations( RD->fields(), CurOffsetInBits, Context, Layout); if (!OffsetAfterFields) - return llvm::None; + return std::nullopt; CurOffsetInBits = *OffsetAfterFields; return CurOffsetInBits; @@ -2839,7 +2849,7 @@ bool ASTContext::hasUniqueObjectRepresentations(QualType Ty) const { if (Record->isUnion()) return unionHasUniqueObjectRepresentations(*this, Record); - Optional<int64_t> StructSize = + std::optional<int64_t> StructSize = structHasUniqueObjectRepresentations(*this, Record); return StructSize && *StructSize == static_cast<int64_t>(getTypeSize(Ty)); @@ -3001,6 +3011,18 @@ ASTContext::getASTObjCImplementationLayout( return getObjCLayout(D->getClassInterface(), D); } +static auto getCanonicalTemplateArguments(const ASTContext &C, + ArrayRef<TemplateArgument> Args, + bool &AnyNonCanonArgs) { + SmallVector<TemplateArgument, 16> CanonArgs(Args); + for (auto &Arg : CanonArgs) { + TemplateArgument OrigArg = Arg; + Arg = C.getCanonicalTemplateArgument(Arg); + AnyNonCanonArgs |= !Arg.structurallyEquals(OrigArg); + } + return CanonArgs; +} + //===----------------------------------------------------------------------===// // Type creation/memoization methods //===----------------------------------------------------------------------===// @@ -3204,9 +3226,9 @@ bool ASTContext::hasSameFunctionTypeIgnoringExceptionSpec(QualType T, QualType ASTContext::getFunctionTypeWithoutPtrSizes(QualType T) { if (const auto *Proto = T->getAs<FunctionProtoType>()) { QualType RetTy = removePtrSizeAddrSpace(Proto->getReturnType()); - SmallVector<QualType, 16> Args(Proto->param_types()); + SmallVector<QualType, 16> Args(Proto->param_types().size()); for (unsigned i = 0, n = Args.size(); i != n; ++i) - Args[i] = removePtrSizeAddrSpace(Args[i]); + Args[i] = removePtrSizeAddrSpace(Proto->param_types()[i]); return getFunctionType(RetTy, Args, Proto->getExtProtoInfo()); } @@ -3329,6 +3351,26 @@ QualType ASTContext::getAdjustedType(QualType Orig, QualType New) const { return QualType(AT, 0); } +QualType ASTContext::getDecayedType(QualType Orig, QualType Decayed) const { + llvm::FoldingSetNodeID ID; + AdjustedType::Profile(ID, Orig, Decayed); + void *InsertPos = nullptr; + AdjustedType *AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos); + if (AT) + return QualType(AT, 0); + + QualType Canonical = getCanonicalType(Decayed); + + // Get the new insert position for the node we care about. + AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!AT && "Shouldn't be in the map!"); + + AT = new (*this, TypeAlignment) DecayedType(Orig, Decayed, Canonical); + Types.push_back(AT); + AdjustedTypes.InsertNode(AT, InsertPos); + return QualType(AT, 0); +} + QualType ASTContext::getDecayedType(QualType T) const { assert((T->isArrayType() || T->isFunctionType()) && "T does not decay"); @@ -3349,23 +3391,7 @@ QualType ASTContext::getDecayedType(QualType T) const { if (T->isFunctionType()) Decayed = getPointerType(T); - llvm::FoldingSetNodeID ID; - AdjustedType::Profile(ID, T, Decayed); - void *InsertPos = nullptr; - AdjustedType *AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos); - if (AT) - return QualType(AT, 0); - - QualType Canonical = getCanonicalType(Decayed); - - // Get the new insert position for the node we care about. - AT = AdjustedTypes.FindNodeOrInsertPos(ID, InsertPos); - assert(!AT && "Shouldn't be in the map!"); - - AT = new (*this, TypeAlignment) DecayedType(T, Decayed, Canonical); - Types.push_back(AT); - AdjustedTypes.InsertNode(AT, InsertPos); - return QualType(AT, 0); + return getDecayedType(T, Decayed); } /// getBlockPointerType - Return the uniqued reference to the type for @@ -3541,6 +3567,7 @@ QualType ASTContext::getConstantArrayType(QualType EltTy, // is instantiation-dependent, this won't be a canonical type either, so fill // in the canonical type field. QualType Canon; + // FIXME: Check below should look for qualifiers behind sugar. if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers() || SizeExpr) { SplitQualType canonSplit = getCanonicalType(EltTy).split(); Canon = getConstantArrayType(QualType(canonSplit.Ty, 0), ArySize, nullptr, @@ -3716,6 +3743,7 @@ QualType ASTContext::getVariableArrayType(QualType EltTy, QualType Canon; // Be sure to pull qualifiers off the element type. + // FIXME: Check below should look for qualifiers behind sugar. if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers()) { SplitQualType canonSplit = getCanonicalType(EltTy).split(); Canon = getVariableArrayType(QualType(canonSplit.Ty, 0), NumElts, ASM, @@ -3818,6 +3846,7 @@ QualType ASTContext::getIncompleteArrayType(QualType elementType, // qualifiers off the element type. QualType canon; + // FIXME: Check below should look for qualifiers behind sugar. if (!elementType.isCanonical() || elementType.hasLocalQualifiers()) { SplitQualType canonSplit = getCanonicalType(elementType).split(); canon = getIncompleteArrayType(QualType(canonSplit.Ty, 0), @@ -4009,7 +4038,11 @@ QualType ASTContext::getScalableVectorType(QualType EltTy, /// the specified element type and size. VectorType must be a built-in type. QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts, VectorType::VectorKind VecKind) const { - assert(vecType->isBuiltinType()); + assert(vecType->isBuiltinType() || + (vecType->isBitIntType() && + // Only support _BitInt elements with byte-sized power of 2 NumBits. + llvm::isPowerOf2_32(vecType->getAs<BitIntType>()->getNumBits()) && + vecType->getAs<BitIntType>()->getNumBits() >= 8)); // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; @@ -4077,9 +4110,13 @@ ASTContext::getDependentVectorType(QualType VecType, Expr *SizeExpr, /// getExtVectorType - Return the unique reference to an extended vector type of /// the specified element type and size. VectorType must be a built-in type. -QualType -ASTContext::getExtVectorType(QualType vecType, unsigned NumElts) const { - assert(vecType->isBuiltinType() || vecType->isDependentType()); +QualType ASTContext::getExtVectorType(QualType vecType, + unsigned NumElts) const { + assert(vecType->isBuiltinType() || vecType->isDependentType() || + (vecType->isBitIntType() && + // Only support _BitInt elements with byte-sized power of 2 NumBits. + llvm::isPowerOf2_32(vecType->getAs<BitIntType>()->getNumBits()) && + vecType->getAs<BitIntType>()->getNumBits() >= 8)); // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; @@ -4420,7 +4457,7 @@ QualType ASTContext::getFunctionTypeInternal( case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated: // We don't know yet. It shouldn't matter what we pick here; no-one // should ever look at this. - LLVM_FALLTHROUGH; + [[fallthrough]]; case EST_None: case EST_MSAny: case EST_NoexceptFalse: CanonicalEPI.ExceptionSpec.Type = EST_None; break; @@ -4626,34 +4663,60 @@ QualType ASTContext::getTypeDeclTypeSlow(const TypeDecl *Decl) const { /// specified typedef name decl. QualType ASTContext::getTypedefType(const TypedefNameDecl *Decl, QualType Underlying) const { - if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0); + if (!Decl->TypeForDecl) { + if (Underlying.isNull()) + Underlying = Decl->getUnderlyingType(); + auto *NewType = new (*this, TypeAlignment) TypedefType( + Type::Typedef, Decl, QualType(), getCanonicalType(Underlying)); + Decl->TypeForDecl = NewType; + Types.push_back(NewType); + return QualType(NewType, 0); + } + if (Underlying.isNull() || Decl->getUnderlyingType() == Underlying) + return QualType(Decl->TypeForDecl, 0); + assert(hasSameType(Decl->getUnderlyingType(), Underlying)); - if (Underlying.isNull()) - Underlying = Decl->getUnderlyingType(); - QualType Canonical = getCanonicalType(Underlying); - auto *newType = new (*this, TypeAlignment) - TypedefType(Type::Typedef, Decl, Underlying, Canonical); - Decl->TypeForDecl = newType; - Types.push_back(newType); - return QualType(newType, 0); + llvm::FoldingSetNodeID ID; + TypedefType::Profile(ID, Decl, Underlying); + + void *InsertPos = nullptr; + if (TypedefType *T = TypedefTypes.FindNodeOrInsertPos(ID, InsertPos)) { + assert(!T->typeMatchesDecl() && + "non-divergent case should be handled with TypeDecl"); + return QualType(T, 0); + } + + void *Mem = + Allocate(TypedefType::totalSizeToAlloc<QualType>(true), TypeAlignment); + auto *NewType = new (Mem) TypedefType(Type::Typedef, Decl, Underlying, + getCanonicalType(Underlying)); + TypedefTypes.InsertNode(NewType, InsertPos); + Types.push_back(NewType); + return QualType(NewType, 0); } QualType ASTContext::getUsingType(const UsingShadowDecl *Found, QualType Underlying) const { llvm::FoldingSetNodeID ID; - UsingType::Profile(ID, Found); + UsingType::Profile(ID, Found, Underlying); void *InsertPos = nullptr; - UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos); - if (T) + if (UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(T, 0); - assert(!Underlying.hasLocalQualifiers()); - assert(Underlying == getTypeDeclType(cast<TypeDecl>(Found->getTargetDecl()))); - QualType Canon = Underlying.getCanonicalType(); + const Type *TypeForDecl = + cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl(); - UsingType *NewType = - new (*this, TypeAlignment) UsingType(Found, Underlying, Canon); + assert(!Underlying.hasLocalQualifiers()); + QualType Canon = Underlying->getCanonicalTypeInternal(); + assert(TypeForDecl->getCanonicalTypeInternal() == Canon); + + if (Underlying.getTypePtr() == TypeForDecl) + Underlying = QualType(); + void *Mem = + Allocate(UsingType::totalSizeToAlloc<QualType>(!Underlying.isNull()), + TypeAlignment); + UsingType *NewType = new (Mem) UsingType(Found, Underlying, Canon); Types.push_back(NewType); UsingTypes.InsertNode(NewType, InsertPos); return QualType(NewType, 0); @@ -4742,21 +4805,22 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr, } /// Retrieve a substitution-result type. -QualType -ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm, - QualType Replacement) const { - assert(Replacement.isCanonical() - && "replacement types must always be canonical"); - +QualType ASTContext::getSubstTemplateTypeParmType( + QualType Replacement, Decl *AssociatedDecl, unsigned Index, + std::optional<unsigned> PackIndex) const { llvm::FoldingSetNodeID ID; - SubstTemplateTypeParmType::Profile(ID, Parm, Replacement); + SubstTemplateTypeParmType::Profile(ID, Replacement, AssociatedDecl, Index, + PackIndex); void *InsertPos = nullptr; - SubstTemplateTypeParmType *SubstParm - = SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos); + SubstTemplateTypeParmType *SubstParm = + SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos); if (!SubstParm) { - SubstParm = new (*this, TypeAlignment) - SubstTemplateTypeParmType(Parm, Replacement); + void *Mem = Allocate(SubstTemplateTypeParmType::totalSizeToAlloc<QualType>( + !Replacement.isCanonical()), + TypeAlignment); + SubstParm = new (Mem) SubstTemplateTypeParmType(Replacement, AssociatedDecl, + Index, PackIndex); Types.push_back(SubstParm); SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos); } @@ -4765,34 +4829,38 @@ ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm, } /// Retrieve a -QualType ASTContext::getSubstTemplateTypeParmPackType( - const TemplateTypeParmType *Parm, - const TemplateArgument &ArgPack) { +QualType +ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl, + unsigned Index, bool Final, + const TemplateArgument &ArgPack) { #ifndef NDEBUG - for (const auto &P : ArgPack.pack_elements()) { - assert(P.getKind() == TemplateArgument::Type &&"Pack contains a non-type"); - assert(P.getAsType().isCanonical() && "Pack contains non-canonical type"); - } + for (const auto &P : ArgPack.pack_elements()) + assert(P.getKind() == TemplateArgument::Type && "Pack contains a non-type"); #endif llvm::FoldingSetNodeID ID; - SubstTemplateTypeParmPackType::Profile(ID, Parm, ArgPack); + SubstTemplateTypeParmPackType::Profile(ID, AssociatedDecl, Index, Final, + ArgPack); void *InsertPos = nullptr; - if (SubstTemplateTypeParmPackType *SubstParm - = SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos)) + if (SubstTemplateTypeParmPackType *SubstParm = + SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(SubstParm, 0); QualType Canon; - if (!Parm->isCanonicalUnqualified()) { - Canon = getCanonicalType(QualType(Parm, 0)); - Canon = getSubstTemplateTypeParmPackType(cast<TemplateTypeParmType>(Canon), - ArgPack); - SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos); + { + TemplateArgument CanonArgPack = getCanonicalTemplateArgument(ArgPack); + if (!AssociatedDecl->isCanonicalDecl() || + !CanonArgPack.structurallyEquals(ArgPack)) { + Canon = getSubstTemplateTypeParmPackType( + AssociatedDecl->getCanonicalDecl(), Index, Final, CanonArgPack); + [[maybe_unused]] const auto *Nothing = + SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!Nothing); + } } - auto *SubstParm - = new (*this, TypeAlignment) SubstTemplateTypeParmPackType(Parm, Canon, - ArgPack); + auto *SubstParm = new (*this, TypeAlignment) SubstTemplateTypeParmPackType( + Canon, AssociatedDecl, Index, Final, ArgPack); Types.push_back(SubstParm); SubstTemplateTypeParmPackTypes.InsertNode(SubstParm, InsertPos); return QualType(SubstParm, 0); @@ -4838,7 +4906,8 @@ ASTContext::getTemplateSpecializationTypeInfo(TemplateName Name, QualType Underlying) const { assert(!Name.getAsDependentTemplateName() && "No dependent template names here!"); - QualType TST = getTemplateSpecializationType(Name, Args, Underlying); + QualType TST = + getTemplateSpecializationType(Name, Args.arguments(), Underlying); TypeSourceInfo *DI = CreateTypeSourceInfo(TST); TemplateSpecializationTypeLoc TL = @@ -4854,14 +4923,14 @@ ASTContext::getTemplateSpecializationTypeInfo(TemplateName Name, QualType ASTContext::getTemplateSpecializationType(TemplateName Template, - const TemplateArgumentListInfo &Args, + ArrayRef<TemplateArgumentLoc> Args, QualType Underlying) const { assert(!Template.getAsDependentTemplateName() && "No dependent template names here!"); SmallVector<TemplateArgument, 4> ArgVec; ArgVec.reserve(Args.size()); - for (const TemplateArgumentLoc &Arg : Args.arguments()) + for (const TemplateArgumentLoc &Arg : Args) ArgVec.push_back(Arg.getArgument()); return getTemplateSpecializationType(Template, ArgVec, Underlying); @@ -4887,8 +4956,8 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) Template = QTN->getUnderlyingTemplate(); - bool IsTypeAlias = - isa_and_nonnull<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()); + const auto *TD = Template.getAsTemplateDecl(); + bool IsTypeAlias = TD && TD->isTypeAlias(); QualType CanonType; if (!Underlying.isNull()) CanonType = getCanonicalType(Underlying); @@ -4916,23 +4985,6 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, return QualType(Spec, 0); } -static bool -getCanonicalTemplateArguments(const ASTContext &C, - ArrayRef<TemplateArgument> OrigArgs, - SmallVectorImpl<TemplateArgument> &CanonArgs) { - bool AnyNonCanonArgs = false; - unsigned NumArgs = OrigArgs.size(); - CanonArgs.resize(NumArgs); - for (unsigned I = 0; I != NumArgs; ++I) { - const TemplateArgument &OrigArg = OrigArgs[I]; - TemplateArgument &CanonArg = CanonArgs[I]; - CanonArg = C.getCanonicalTemplateArgument(OrigArg); - if (!CanonArg.structurallyEquals(OrigArg)) - AnyNonCanonArgs = true; - } - return AnyNonCanonArgs; -} - QualType ASTContext::getCanonicalTemplateSpecializationType( TemplateName Template, ArrayRef<TemplateArgument> Args) const { assert(!Template.getAsDependentTemplateName() && @@ -4944,8 +4996,9 @@ QualType ASTContext::getCanonicalTemplateSpecializationType( // Build the canonical template specialization type. TemplateName CanonTemplate = getCanonicalTemplateName(Template); - SmallVector<TemplateArgument, 4> CanonArgs; - ::getCanonicalTemplateArguments(*this, Args, CanonArgs); + bool AnyNonCanonArgs = false; + auto CanonArgs = + ::getCanonicalTemplateArguments(*this, Args, AnyNonCanonArgs); // Determine whether this canonical template specialization type already // exists. @@ -5065,12 +5118,9 @@ QualType ASTContext::getDependentNameType(ElaboratedTypeKeyword Keyword, return QualType(T, 0); } -QualType -ASTContext::getDependentTemplateSpecializationType( - ElaboratedTypeKeyword Keyword, - NestedNameSpecifier *NNS, - const IdentifierInfo *Name, - const TemplateArgumentListInfo &Args) const { +QualType ASTContext::getDependentTemplateSpecializationType( + ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS, + const IdentifierInfo *Name, ArrayRef<TemplateArgumentLoc> Args) const { // TODO: avoid this copy SmallVector<TemplateArgument, 16> ArgCopy; for (unsigned I = 0, E = Args.size(); I != E; ++I) @@ -5102,9 +5152,9 @@ ASTContext::getDependentTemplateSpecializationType( ElaboratedTypeKeyword CanonKeyword = Keyword; if (Keyword == ETK_None) CanonKeyword = ETK_Typename; - SmallVector<TemplateArgument, 16> CanonArgs; - bool AnyNonCanonArgs = - ::getCanonicalTemplateArguments(*this, Args, CanonArgs); + bool AnyNonCanonArgs = false; + auto CanonArgs = + ::getCanonicalTemplateArguments(*this, Args, AnyNonCanonArgs); QualType Canon; if (AnyNonCanonArgs || CanonNNS != NNS || CanonKeyword != Keyword) { @@ -5113,7 +5163,9 @@ ASTContext::getDependentTemplateSpecializationType( CanonArgs); // Find the insert position again. - DependentTemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos); + [[maybe_unused]] auto *Nothing = + DependentTemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!Nothing && "canonical type broken"); } void *Mem = Allocate((sizeof(DependentTemplateSpecializationType) + @@ -5131,7 +5183,7 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) { if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) { QualType ArgType = getTypeDeclType(TTP); if (TTP->isParameterPack()) - ArgType = getPackExpansionType(ArgType, None); + ArgType = getPackExpansionType(ArgType, std::nullopt); Arg = TemplateArgument(ArgType); } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { @@ -5144,17 +5196,17 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) { if (T->isRecordType()) T.addConst(); Expr *E = new (*this) DeclRefExpr( - *this, NTTP, /*enclosing*/ false, T, + *this, NTTP, /*RefersToEnclosingVariableOrCapture*/ false, T, Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation()); if (NTTP->isParameterPack()) - E = new (*this) PackExpansionExpr(DependentTy, E, NTTP->getLocation(), - None); + E = new (*this) + PackExpansionExpr(DependentTy, E, NTTP->getLocation(), std::nullopt); Arg = TemplateArgument(E); } else { auto *TTP = cast<TemplateTemplateParmDecl>(Param); if (TTP->isParameterPack()) - Arg = TemplateArgument(TemplateName(TTP), Optional<unsigned>()); + Arg = TemplateArgument(TemplateName(TTP), std::optional<unsigned>()); else Arg = TemplateArgument(TemplateName(TTP)); } @@ -5175,7 +5227,7 @@ ASTContext::getInjectedTemplateArgs(const TemplateParameterList *Params, } QualType ASTContext::getPackExpansionType(QualType Pattern, - Optional<unsigned> NumExpansions, + std::optional<unsigned> NumExpansions, bool ExpectPackInType) { assert((!ExpectPackInType || Pattern->containsUnexpandedParameterPack()) && "Pack expansions must expand one or more parameter packs"); @@ -5243,7 +5295,7 @@ QualType ASTContext::getObjCObjectType(QualType BaseType, ObjCProtocolDecl * const *Protocols, unsigned NumProtocols) const { return getObjCObjectType(BaseType, {}, - llvm::makeArrayRef(Protocols, NumProtocols), + llvm::ArrayRef(Protocols, NumProtocols), /*isKindOf=*/false); } @@ -5565,30 +5617,31 @@ QualType ASTContext::getObjCInterfaceType(const ObjCInterfaceDecl *Decl, /// multiple declarations that refer to "typeof(x)" all contain different /// DeclRefExpr's. This doesn't effect the type checker, since it operates /// on canonical type's (which are always unique). -QualType ASTContext::getTypeOfExprType(Expr *tofExpr) const { +QualType ASTContext::getTypeOfExprType(Expr *tofExpr, TypeOfKind Kind) const { TypeOfExprType *toe; if (tofExpr->isTypeDependent()) { llvm::FoldingSetNodeID ID; - DependentTypeOfExprType::Profile(ID, *this, tofExpr); + DependentTypeOfExprType::Profile(ID, *this, tofExpr, + Kind == TypeOfKind::Unqualified); void *InsertPos = nullptr; - DependentTypeOfExprType *Canon - = DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos); + DependentTypeOfExprType *Canon = + DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos); if (Canon) { // We already have a "canonical" version of an identical, dependent // typeof(expr) type. Use that as our canonical type. - toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, - QualType((TypeOfExprType*)Canon, 0)); + toe = new (*this, TypeAlignment) + TypeOfExprType(tofExpr, Kind, QualType((TypeOfExprType *)Canon, 0)); } else { // Build a new, canonical typeof(expr) type. - Canon - = new (*this, TypeAlignment) DependentTypeOfExprType(*this, tofExpr); + Canon = new (*this, TypeAlignment) + DependentTypeOfExprType(*this, tofExpr, Kind); DependentTypeOfExprTypes.InsertNode(Canon, InsertPos); toe = Canon; } } else { QualType Canonical = getCanonicalType(tofExpr->getType()); - toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, Canonical); + toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, Kind, Canonical); } Types.push_back(toe); return QualType(toe, 0); @@ -5599,9 +5652,10 @@ QualType ASTContext::getTypeOfExprType(Expr *tofExpr) const { /// memory savings. Since typeof(t) is fairly uncommon, space shouldn't be /// an issue. This doesn't affect the type checker, since it operates /// on canonical types (which are always unique). -QualType ASTContext::getTypeOfType(QualType tofType) const { +QualType ASTContext::getTypeOfType(QualType tofType, TypeOfKind Kind) const { QualType Canonical = getCanonicalType(tofType); - auto *tot = new (*this, TypeAlignment) TypeOfType(tofType, Canonical); + auto *tot = + new (*this, TypeAlignment) TypeOfType(tofType, Canonical, Kind); Types.push_back(tot); return QualType(tot, 0); } @@ -5707,9 +5761,6 @@ QualType ASTContext::getAutoTypeInternal( !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); - if (TypeConstraintConcept) - TypeConstraintConcept = TypeConstraintConcept->getCanonicalDecl(); - // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; @@ -5720,18 +5771,15 @@ QualType ASTContext::getAutoTypeInternal( QualType Canon; if (!IsCanon) { - if (DeducedType.isNull()) { - SmallVector<TemplateArgument, 4> CanonArgs; - bool AnyNonCanonArgs = - ::getCanonicalTemplateArguments(*this, TypeConstraintArgs, CanonArgs); - if (AnyNonCanonArgs) { - Canon = getAutoTypeInternal(QualType(), Keyword, IsDependent, IsPack, - TypeConstraintConcept, CanonArgs, true); - // Find the insert position again. - AutoTypes.FindNodeOrInsertPos(ID, InsertPos); - } - } else { + if (!DeducedType.isNull()) { Canon = DeducedType.getCanonicalType(); + } else if (TypeConstraintConcept) { + Canon = getAutoTypeInternal(QualType(), Keyword, IsDependent, IsPack, + nullptr, {}, true); + // Find the insert position again. + [[maybe_unused]] auto *Nothing = + AutoTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!Nothing && "canonical type broken"); } } @@ -5892,14 +5940,14 @@ QualType ASTContext::getUIntPtrType() const { /// getPointerDiffType - Return the unique type for "ptrdiff_t" (C99 7.17) /// defined in <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9). QualType ASTContext::getPointerDiffType() const { - return getFromTargetType(Target->getPtrDiffType(0)); + return getFromTargetType(Target->getPtrDiffType(LangAS::Default)); } /// Return the unique unsigned counterpart of "ptrdiff_t" /// integer type. The standard (C11 7.21.6.1p7) refers to this type /// in the definition of %tu format specifier. QualType ASTContext::getUnsignedPointerDiffType() const { - return getFromTargetType(Target->getUnsignedPtrDiffType(0)); + return getFromTargetType(Target->getUnsignedPtrDiffType(LangAS::Default)); } /// Return the unique type for "pid_t" defined in @@ -6199,13 +6247,13 @@ ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { } case TemplateName::SubstTemplateTemplateParmPack: { - SubstTemplateTemplateParmPackStorage *subst - = Name.getAsSubstTemplateTemplateParmPack(); - TemplateTemplateParmDecl *canonParameter - = getCanonicalTemplateTemplateParmDecl(subst->getParameterPack()); - TemplateArgument canonArgPack - = getCanonicalTemplateArgument(subst->getArgumentPack()); - return getSubstTemplateTemplateParmPack(canonParameter, canonArgPack); + SubstTemplateTemplateParmPackStorage *subst = + Name.getAsSubstTemplateTemplateParmPack(); + TemplateArgument canonArgPack = + getCanonicalTemplateArgument(subst->getArgumentPack()); + return getSubstTemplateTemplateParmPack( + canonArgPack, subst->getAssociatedDecl()->getCanonicalDecl(), + subst->getFinal(), subst->getIndex()); } } @@ -6287,7 +6335,9 @@ bool ASTContext::isSameTemplateParameter(const NamedDecl *X, if (auto *TX = dyn_cast<NonTypeTemplateParmDecl>(X)) { auto *TY = cast<NonTypeTemplateParmDecl>(Y); return TX->isParameterPack() == TY->isParameterPack() && - TX->getASTContext().hasSameType(TX->getType(), TY->getType()); + TX->getASTContext().hasSameType(TX->getType(), TY->getType()) && + isSameConstraintExpr(TX->getPlaceholderTypeConstraint(), + TY->getPlaceholderTypeConstraint()); } auto *TX = cast<TemplateTemplateParmDecl>(X); @@ -6407,8 +6457,8 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A, auto BEnableIfAttrs = B->specific_attrs<EnableIfAttr>(); for (auto Pair : zip_longest(AEnableIfAttrs, BEnableIfAttrs)) { - Optional<EnableIfAttr *> Cand1A = std::get<0>(Pair); - Optional<EnableIfAttr *> Cand2A = std::get<1>(Pair); + std::optional<EnableIfAttr *> Cand1A = std::get<0>(Pair); + std::optional<EnableIfAttr *> Cand2A = std::get<1>(Pair); // Return false if the number of enable_if attributes is different. if (!Cand1A || !Cand2A) @@ -6428,6 +6478,31 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A, return true; } +bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X, + const FunctionDecl *Y) const { + // If these aren't friends, then they aren't friends that differ by + // constraints. + if (!X->getFriendObjectKind() || !Y->getFriendObjectKind()) + return false; + + // If the two functions share lexical declaration context, they are not in + // separate instantations, and thus in the same scope. + if (X->getLexicalDeclContext() == Y->getLexicalDeclContext()) + return false; + + if (!X->getDescribedFunctionTemplate()) { + assert(!Y->getDescribedFunctionTemplate() && + "How would these be the same if they aren't both templates?"); + + // If these friends don't have constraints, they aren't constrained, and + // thus don't fall under temp.friend p9. Else the simple presence of a + // constraint makes them unique. + return X->getTrailingRequiresClause(); + } + + return X->FriendConstraintRefersToEnclosingTemplate(); +} + bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const { if (X == Y) return true; @@ -6508,6 +6583,10 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const { FuncY->getTrailingRequiresClause())) return false; + // Constrained friends are different in certain cases, see: [temp.friend]p9. + if (FriendsDifferByConstraints(FuncX, FuncY)) + return false; + auto GetTypeAsWritten = [](const FunctionDecl *FD) { // Map to the first declaration that we've already merged into this one. // The TSI of redeclarations might not match (due to calling conventions @@ -6660,7 +6739,7 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { case TemplateArgument::Declaration: { auto *D = cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl()); - return TemplateArgument(D, Arg.getParamTypeForDecl()); + return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl())); } case TemplateArgument::NullPtr: @@ -6682,17 +6761,13 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { return TemplateArgument(getCanonicalType(Arg.getAsType())); case TemplateArgument::Pack: { - if (Arg.pack_size() == 0) + bool AnyNonCanonArgs = false; + auto CanonArgs = ::getCanonicalTemplateArguments( + *this, Arg.pack_elements(), AnyNonCanonArgs); + if (!AnyNonCanonArgs) return Arg; - - auto *CanonArgs = new (*this) TemplateArgument[Arg.pack_size()]; - unsigned Idx = 0; - for (TemplateArgument::pack_iterator A = Arg.pack_begin(), - AEnd = Arg.pack_end(); - A != AEnd; (void)++A, ++Idx) - CanonArgs[Idx] = getCanonicalTemplateArgument(*A); - - return TemplateArgument(llvm::makeArrayRef(CanonArgs, Arg.pack_size())); + return TemplateArgument::CreatePackCopy(const_cast<ASTContext &>(*this), + CanonArgs); } } @@ -6864,7 +6939,7 @@ QualType ASTContext::getArrayDecayedType(QualType Ty) const { PrettyArrayType->getIndexTypeQualifiers()); // int x[_Nullable] -> int * _Nullable - if (auto Nullability = Ty->getNullability(*this)) { + if (auto Nullability = Ty->getNullability()) { Result = const_cast<ASTContext *>(this)->getAttributedType( AttributedType::getNullabilityAttrKind(*Nullability), Result, Result); } @@ -6901,6 +6976,21 @@ ASTContext::getConstantArrayElementCount(const ConstantArrayType *CA) const { return ElementCount; } +uint64_t ASTContext::getArrayInitLoopExprElementCount( + const ArrayInitLoopExpr *AILE) const { + if (!AILE) + return 0; + + uint64_t ElementCount = 1; + + do { + ElementCount *= AILE->getArraySize().getZExtValue(); + AILE = dyn_cast<ArrayInitLoopExpr>(AILE->getSubExpr()); + } while (AILE); + + return ElementCount; +} + /// getFloatingRank - Return a relative rank for floating point types. /// This routine will assert if passed a built-in type that isn't a float. static FloatingRank getFloatingRank(QualType T) { @@ -6976,6 +7066,21 @@ unsigned ASTContext::getIntegerRank(const Type *T) const { case BuiltinType::Int128: case BuiltinType::UInt128: return 7 + (getIntWidth(Int128Ty) << 3); + + // "The ranks of char8_t, char16_t, char32_t, and wchar_t equal the ranks of + // their underlying types" [c++20 conv.rank] + case BuiltinType::Char8: + return getIntegerRank(UnsignedCharTy.getTypePtr()); + case BuiltinType::Char16: + return getIntegerRank( + getFromTargetType(Target->getChar16Type()).getTypePtr()); + case BuiltinType::Char32: + return getIntegerRank( + getFromTargetType(Target->getChar32Type()).getTypePtr()); + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + return getIntegerRank( + getFromTargetType(Target->getWCharType()).getTypePtr()); } } @@ -7041,7 +7146,7 @@ QualType ASTContext::isPromotableBitField(Expr *E) const { /// integer type. QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { assert(!Promotable.isNull()); - assert(Promotable->isPromotableIntegerType()); + assert(isPromotableIntegerType(Promotable)); if (const auto *ET = Promotable->getAs<EnumType>()) return ET->getDecl()->getPromotionType(); @@ -7061,12 +7166,11 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { uint64_t FromSize = getTypeSize(BT); QualType PromoteTypes[] = { IntTy, UnsignedIntTy, LongTy, UnsignedLongTy, LongLongTy, UnsignedLongLongTy }; - for (size_t Idx = 0; Idx < llvm::array_lengthof(PromoteTypes); ++Idx) { - uint64_t ToSize = getTypeSize(PromoteTypes[Idx]); + for (const auto &PT : PromoteTypes) { + uint64_t ToSize = getTypeSize(PT); if (FromSize < ToSize || - (FromSize == ToSize && - FromIsSigned == PromoteTypes[Idx]->isSignedIntegerType())) - return PromoteTypes[Idx]; + (FromSize == ToSize && FromIsSigned == PT->isSignedIntegerType())) + return PT; } llvm_unreachable("char type should fit into long long"); } @@ -7551,7 +7655,7 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { // FIXME: There might(should) be a better way of doing this computation! CharUnits PtrSize = getTypeSizeInChars(VoidPtrTy); CharUnits ParmOffset = PtrSize; - for (auto PI : Decl->parameters()) { + for (auto *PI : Decl->parameters()) { QualType PType = PI->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) @@ -7566,7 +7670,7 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { // Argument types. ParmOffset = PtrSize; - for (auto PVDecl : Decl->parameters()) { + for (auto *PVDecl : Decl->parameters()) { QualType PType = PVDecl->getOriginalType(); if (const auto *AT = dyn_cast<ArrayType>(PType->getCanonicalTypeInternal())) { @@ -7595,7 +7699,7 @@ ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl) const { getObjCEncodingForType(Decl->getReturnType(), S); CharUnits ParmOffset; // Compute size of all parameters. - for (auto PI : Decl->parameters()) { + for (auto *PI : Decl->parameters()) { QualType PType = PI->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) @@ -7609,7 +7713,7 @@ ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl) const { ParmOffset = CharUnits::Zero(); // Argument types. - for (auto PVDecl : Decl->parameters()) { + for (auto *PVDecl : Decl->parameters()) { QualType PType = PVDecl->getOriginalType(); if (const auto *AT = dyn_cast<ArrayType>(PType->getCanonicalTypeInternal())) { @@ -7816,7 +7920,7 @@ ASTContext::getObjCEncodingForPropertyDecl(const ObjCPropertyDecl *PD, /// 'l' or 'L' , but not always. For typedefs, we need to use /// 'i' or 'I' instead if encoding a struct field, or a pointer! void ASTContext::getLegacyIntegralTypeEncoding (QualType &PointeeTy) const { - if (isa<TypedefType>(PointeeTy.getTypePtr())) { + if (PointeeTy->getAs<TypedefType>()) { if (const auto *BT = PointeeTy->getAs<BuiltinType>()) { if (BT->getKind() == BuiltinType::ULong && getIntWidth(PointeeTy) == 32) PointeeTy = UnsignedIntTy; @@ -8104,7 +8208,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, // pointee gets emitted _before_ the '^'. The read-only qualifier of // the pointer itself gets ignored, _unless_ we are looking at a typedef! // Also, do not emit the 'r' for anything but the outermost type! - if (isa<TypedefType>(T.getTypePtr())) { + if (T->getAs<TypedefType>()) { if (Options.IsOutermostType() && T.isConstQualified()) { isReadOnly = true; S += 'r'; @@ -8286,7 +8390,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, return; } // TODO: Double check to make sure this intentionally falls through. - LLVM_FALLTHROUGH; + [[fallthrough]]; } case Type::ObjCInterface: { @@ -8641,9 +8745,9 @@ CreateAArch64ABIBuiltinVaListDecl(const ASTContext *Context) { // namespace std { struct __va_list { auto *NS = NamespaceDecl::Create( const_cast<ASTContext &>(*Context), Context->getTranslationUnitDecl(), - /*Inline*/ false, SourceLocation(), SourceLocation(), + /*Inline=*/false, SourceLocation(), SourceLocation(), &Context->Idents.get("std"), - /*PrevDecl*/ nullptr); + /*PrevDecl=*/nullptr, /*Nested=*/false); NS->setImplicit(); VaListTagDecl->setDeclContext(NS); } @@ -8830,9 +8934,9 @@ CreateAAPCSABIBuiltinVaListDecl(const ASTContext *Context) { NamespaceDecl *NS; NS = NamespaceDecl::Create(const_cast<ASTContext &>(*Context), Context->getTranslationUnitDecl(), - /*Inline*/false, SourceLocation(), + /*Inline=*/false, SourceLocation(), SourceLocation(), &Context->Idents.get("std"), - /*PrevDecl*/ nullptr); + /*PrevDecl=*/nullptr, /*Nested=*/false); NS->setImplicit(); VaListDecl->setDeclContext(NS); } @@ -9020,6 +9124,10 @@ TypedefDecl *ASTContext::getBuiltinMSVaListDecl() const { } bool ASTContext::canBuiltinBeRedeclared(const FunctionDecl *FD) const { + // Allow redecl custom type checking builtin for HLSL. + if (LangOpts.HLSL && FD->getBuiltinID() != Builtin::NotBuiltin && + BuiltinInfo.hasCustomTypechecking(FD->getBuiltinID())) + return true; return BuiltinInfo.canBeRedeclared(FD->getBuiltinID()); } @@ -9158,18 +9266,20 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, return TemplateName(QTN); } -TemplateName -ASTContext::getSubstTemplateTemplateParm(TemplateTemplateParmDecl *param, - TemplateName replacement) const { +TemplateName ASTContext::getSubstTemplateTemplateParm( + TemplateName Replacement, Decl *AssociatedDecl, unsigned Index, + std::optional<unsigned> PackIndex) const { llvm::FoldingSetNodeID ID; - SubstTemplateTemplateParmStorage::Profile(ID, param, replacement); + SubstTemplateTemplateParmStorage::Profile(ID, Replacement, AssociatedDecl, + Index, PackIndex); void *insertPos = nullptr; SubstTemplateTemplateParmStorage *subst = SubstTemplateTemplateParms.FindNodeOrInsertPos(ID, insertPos); if (!subst) { - subst = new (*this) SubstTemplateTemplateParmStorage(param, replacement); + subst = new (*this) SubstTemplateTemplateParmStorage( + Replacement, AssociatedDecl, Index, PackIndex); SubstTemplateTemplateParms.InsertNode(subst, insertPos); } @@ -9177,20 +9287,21 @@ ASTContext::getSubstTemplateTemplateParm(TemplateTemplateParmDecl *param, } TemplateName -ASTContext::getSubstTemplateTemplateParmPack(TemplateTemplateParmDecl *Param, - const TemplateArgument &ArgPack) const { +ASTContext::getSubstTemplateTemplateParmPack(const TemplateArgument &ArgPack, + Decl *AssociatedDecl, + unsigned Index, bool Final) const { auto &Self = const_cast<ASTContext &>(*this); llvm::FoldingSetNodeID ID; - SubstTemplateTemplateParmPackStorage::Profile(ID, Self, Param, ArgPack); + SubstTemplateTemplateParmPackStorage::Profile(ID, Self, ArgPack, + AssociatedDecl, Index, Final); void *InsertPos = nullptr; SubstTemplateTemplateParmPackStorage *Subst = SubstTemplateTemplateParmPacks.FindNodeOrInsertPos(ID, InsertPos); if (!Subst) { - Subst = new (*this) SubstTemplateTemplateParmPackStorage(Param, - ArgPack.pack_size(), - ArgPack.pack_begin()); + Subst = new (*this) SubstTemplateTemplateParmPackStorage( + ArgPack.pack_elements(), AssociatedDecl, Index, Final); SubstTemplateTemplateParmPacks.InsertNode(Subst, InsertPos); } @@ -9707,7 +9818,7 @@ void getIntersectionOfProtocols(ASTContext &Context, llvm::SmallPtrSet<ObjCProtocolDecl *, 8> LHSProtocolSet; // Start with the protocol qualifiers. - for (auto proto : LHS->quals()) { + for (auto *proto : LHS->quals()) { Context.CollectInheritedProtocols(proto, LHSProtocolSet); } @@ -9718,7 +9829,7 @@ void getIntersectionOfProtocols(ASTContext &Context, llvm::SmallPtrSet<ObjCProtocolDecl *, 8> RHSProtocolSet; // Start with the protocol qualifiers. - for (auto proto : RHS->quals()) { + for (auto *proto : RHS->quals()) { Context.CollectInheritedProtocols(proto, RHSProtocolSet); } @@ -9726,7 +9837,7 @@ void getIntersectionOfProtocols(ASTContext &Context, Context.CollectInheritedProtocols(RHS->getInterface(), RHSProtocolSet); // Compute the intersection of the collected protocol sets. - for (auto proto : LHSProtocolSet) { + for (auto *proto : LHSProtocolSet) { if (RHSProtocolSet.count(proto)) IntersectionSet.push_back(proto); } @@ -10077,7 +10188,8 @@ QualType ASTContext::mergeFunctionParameterTypes(QualType lhs, QualType rhs, QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, bool OfBlockPointer, bool Unqualified, - bool AllowCXX) { + bool AllowCXX, + bool IsConditionalOperator) { const auto *lbase = lhs->castAs<FunctionType>(); const auto *rbase = rhs->castAs<FunctionType>(); const auto *lproto = dyn_cast<FunctionProtoType>(lbase); @@ -10140,9 +10252,27 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck()) return {}; - // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. - bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); - + // When merging declarations, it's common for supplemental information like + // attributes to only be present in one of the declarations, and we generally + // want type merging to preserve the union of information. So a merged + // function type should be noreturn if it was noreturn in *either* operand + // type. + // + // But for the conditional operator, this is backwards. The result of the + // operator could be either operand, and its type should conservatively + // reflect that. So a function type in a composite type is noreturn only + // if it's noreturn in *both* operand types. + // + // Arguably, noreturn is a kind of subtype, and the conditional operator + // ought to produce the most specific common supertype of its operand types. + // That would differ from this rule in contravariant positions. However, + // neither C nor C++ generally uses this kind of subtype reasoning. Also, + // as a practical matter, it would only affect C code that does abstraction of + // higher-order functions (taking noreturn callbacks!), which is uncommon to + // say the least. So we use the simpler rule. + bool NoReturn = IsConditionalOperator + ? lbaseInfo.getNoReturn() && rbaseInfo.getNoReturn() + : lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); if (lbaseInfo.getNoReturn() != NoReturn) allLTypes = false; if (rbaseInfo.getNoReturn() != NoReturn) @@ -10235,7 +10365,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, return {}; } - if (paramTy->isPromotableIntegerType() || + if (isPromotableIntegerType(paramTy) || getCanonicalType(paramTy).getUnqualifiedType() == FloatTy) return {}; } @@ -10275,9 +10405,9 @@ static QualType mergeEnumWithInteger(ASTContext &Context, const EnumType *ET, return {}; } -QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, - bool OfBlockPointer, - bool Unqualified, bool BlockReturnType) { +QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, + bool Unqualified, bool BlockReturnType, + bool IsConditionalOperator) { // For C++ we will not reach this code with reference types (see below), // for OpenMP variant call overloading we might. // @@ -10517,7 +10647,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, const ConstantArrayType* CAT) -> std::pair<bool,llvm::APInt> { if (VAT) { - Optional<llvm::APSInt> TheInt; + std::optional<llvm::APSInt> TheInt; Expr *E = VAT->getSizeExpr(); if (E && (TheInt = E->getIntegerConstantExpr(*this))) return std::make_pair(true, *TheInt); @@ -10570,7 +10700,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, ArrayType::ArraySizeModifier(), 0); } case Type::FunctionNoProto: - return mergeFunctionTypes(LHS, RHS, OfBlockPointer, Unqualified); + return mergeFunctionTypes(LHS, RHS, OfBlockPointer, Unqualified, + /*AllowCXX=*/false, IsConditionalOperator); case Type::Record: case Type::Enum: return {}; @@ -10778,7 +10909,8 @@ unsigned ASTContext::getIntWidth(QualType T) const { } QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { - assert((T->hasSignedIntegerRepresentation() || T->isSignedFixedPointType()) && + assert((T->hasIntegerRepresentation() || T->isEnumeralType() || + T->isFixedPointType()) && "Unexpected type"); // Turn <4 x signed int> -> <4 x unsigned int> @@ -10796,8 +10928,11 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { T = ETy->getDecl()->getIntegerType(); switch (T->castAs<BuiltinType>()->getKind()) { + case BuiltinType::Char_U: + // Plain `char` is mapped to `unsigned char` even if it's already unsigned case BuiltinType::Char_S: case BuiltinType::SChar: + case BuiltinType::Char8: return UnsignedCharTy; case BuiltinType::Short: return UnsignedShortTy; @@ -10811,7 +10946,7 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { return UnsignedInt128Ty; // wchar_t is special. It is either signed or not, but when it's signed, // there's no matching "unsigned wchar_t". Therefore we return the unsigned - // version of it's underlying type instead. + // version of its underlying type instead. case BuiltinType::WChar_S: return getUnsignedWCharType(); @@ -10840,13 +10975,16 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { case BuiltinType::SatLongFract: return SatUnsignedLongFractTy; default: - llvm_unreachable("Unexpected signed integer or fixed point type"); + assert((T->hasUnsignedIntegerRepresentation() || + T->isUnsignedFixedPointType()) && + "Unexpected signed integer or fixed point type"); + return T; } } QualType ASTContext::getCorrespondingSignedType(QualType T) const { - assert((T->hasUnsignedIntegerRepresentation() || - T->isUnsignedFixedPointType()) && + assert((T->hasIntegerRepresentation() || T->isEnumeralType() || + T->isFixedPointType()) && "Unexpected type"); // Turn <4 x unsigned int> -> <4 x signed int> @@ -10864,8 +11002,11 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const { T = ETy->getDecl()->getIntegerType(); switch (T->castAs<BuiltinType>()->getKind()) { + case BuiltinType::Char_S: + // Plain `char` is mapped to `signed char` even if it's already signed case BuiltinType::Char_U: case BuiltinType::UChar: + case BuiltinType::Char8: return SignedCharTy; case BuiltinType::UShort: return ShortTy; @@ -10879,7 +11020,7 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const { return Int128Ty; // wchar_t is special. It is either unsigned or not, but when it's unsigned, // there's no matching "signed wchar_t". Therefore we return the signed - // version of it's underlying type instead. + // version of its underlying type instead. case BuiltinType::WChar_U: return getSignedWCharType(); @@ -10908,7 +11049,10 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const { case BuiltinType::SatULongFract: return SatLongFractTy; default: - llvm_unreachable("Unexpected unsigned integer or fixed point type"); + assert( + (T->hasSignedIntegerRepresentation() || T->isSignedFixedPointType()) && + "Unexpected signed integer or fixed point type"); + return T; } } @@ -11673,7 +11817,7 @@ void ASTContext::forEachMultiversionedFunctionVersion( FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) { FunctionDecl *CurFD = CurDecl->getAsFunction()->getMostRecentDecl(); if (CurFD && hasSameType(CurFD->getType(), FD->getType()) && - std::end(SeenDecls) == llvm::find(SeenDecls, CurFD)) { + !llvm::is_contained(SeenDecls, CurFD)) { SeenDecls.insert(CurFD); Pred(CurFD); } @@ -11775,10 +11919,10 @@ MangleContext *ASTContext::createDeviceMangleContext(const TargetInfo &T) { case TargetCXXABI::XL: return ItaniumMangleContext::create( *this, getDiagnostics(), - [](ASTContext &, const NamedDecl *ND) -> llvm::Optional<unsigned> { + [](ASTContext &, const NamedDecl *ND) -> std::optional<unsigned> { if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) return RD->getDeviceLambdaManglingNumber(); - return llvm::None; + return std::nullopt; }, /*IsAux=*/true); case TargetCXXABI::Microsoft: @@ -12082,25 +12226,885 @@ uint64_t ASTContext::getTargetNullPointerValue(QualType QT) const { return getTargetInfo().getNullPointerValue(AS); } -unsigned ASTContext::getTargetAddressSpace(QualType T) const { - // Return the address space for the type. If the type is a - // function type without an address space qualifier, the - // program address space is used. Otherwise, the target picks - // the best address space based on the type information - return T->isFunctionType() && !T.hasAddressSpace() - ? getTargetInfo().getProgramAddressSpace() - : getTargetAddressSpace(T.getQualifiers()); +unsigned ASTContext::getTargetAddressSpace(LangAS AS) const { + return getTargetInfo().getTargetAddressSpace(AS); } -unsigned ASTContext::getTargetAddressSpace(Qualifiers Q) const { - return getTargetAddressSpace(Q.getAddressSpace()); +bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y) const { + if (X == Y) + return true; + if (!X || !Y) + return false; + llvm::FoldingSetNodeID IDX, IDY; + X->Profile(IDX, *this, /*Canonical=*/true); + Y->Profile(IDY, *this, /*Canonical=*/true); + return IDX == IDY; } -unsigned ASTContext::getTargetAddressSpace(LangAS AS) const { - if (isTargetAddressSpace(AS)) - return toTargetAddressSpace(AS); +// The getCommon* helpers return, for given 'same' X and Y entities given as +// inputs, another entity which is also the 'same' as the inputs, but which +// is closer to the canonical form of the inputs, each according to a given +// criteria. +// The getCommon*Checked variants are 'null inputs not-allowed' equivalents of +// the regular ones. + +static Decl *getCommonDecl(Decl *X, Decl *Y) { + if (!declaresSameEntity(X, Y)) + return nullptr; + for (const Decl *DX : X->redecls()) { + // If we reach Y before reaching the first decl, that means X is older. + if (DX == Y) + return X; + // If we reach the first decl, then Y is older. + if (DX->isFirstDecl()) + return Y; + } + llvm_unreachable("Corrupt redecls chain"); +} + +template <class T, std::enable_if_t<std::is_base_of_v<Decl, T>, bool> = true> +static T *getCommonDecl(T *X, T *Y) { + return cast_or_null<T>( + getCommonDecl(const_cast<Decl *>(cast_or_null<Decl>(X)), + const_cast<Decl *>(cast_or_null<Decl>(Y)))); +} + +template <class T, std::enable_if_t<std::is_base_of_v<Decl, T>, bool> = true> +static T *getCommonDeclChecked(T *X, T *Y) { + return cast<T>(getCommonDecl(const_cast<Decl *>(cast<Decl>(X)), + const_cast<Decl *>(cast<Decl>(Y)))); +} + +static TemplateName getCommonTemplateName(ASTContext &Ctx, TemplateName X, + TemplateName Y) { + if (X.getAsVoidPointer() == Y.getAsVoidPointer()) + return X; + // FIXME: There are cases here where we could find a common template name + // with more sugar. For example one could be a SubstTemplateTemplate* + // replacing the other. + TemplateName CX = Ctx.getCanonicalTemplateName(X); + if (CX.getAsVoidPointer() != + Ctx.getCanonicalTemplateName(Y).getAsVoidPointer()) + return TemplateName(); + return CX; +} + +static TemplateName +getCommonTemplateNameChecked(ASTContext &Ctx, TemplateName X, TemplateName Y) { + TemplateName R = getCommonTemplateName(Ctx, X, Y); + assert(R.getAsVoidPointer() != nullptr); + return R; +} + +static auto getCommonTypes(ASTContext &Ctx, ArrayRef<QualType> Xs, + ArrayRef<QualType> Ys, bool Unqualified = false) { + assert(Xs.size() == Ys.size()); + SmallVector<QualType, 8> Rs(Xs.size()); + for (size_t I = 0; I < Rs.size(); ++I) + Rs[I] = Ctx.getCommonSugaredType(Xs[I], Ys[I], Unqualified); + return Rs; +} + +template <class T> +static SourceLocation getCommonAttrLoc(const T *X, const T *Y) { + return X->getAttributeLoc() == Y->getAttributeLoc() ? X->getAttributeLoc() + : SourceLocation(); +} + +static TemplateArgument getCommonTemplateArgument(ASTContext &Ctx, + const TemplateArgument &X, + const TemplateArgument &Y) { + if (X.getKind() != Y.getKind()) + return TemplateArgument(); + + switch (X.getKind()) { + case TemplateArgument::ArgKind::Type: + if (!Ctx.hasSameType(X.getAsType(), Y.getAsType())) + return TemplateArgument(); + return TemplateArgument( + Ctx.getCommonSugaredType(X.getAsType(), Y.getAsType())); + case TemplateArgument::ArgKind::NullPtr: + if (!Ctx.hasSameType(X.getNullPtrType(), Y.getNullPtrType())) + return TemplateArgument(); + return TemplateArgument( + Ctx.getCommonSugaredType(X.getNullPtrType(), Y.getNullPtrType()), + /*Unqualified=*/true); + case TemplateArgument::ArgKind::Expression: + if (!Ctx.hasSameType(X.getAsExpr()->getType(), Y.getAsExpr()->getType())) + return TemplateArgument(); + // FIXME: Try to keep the common sugar. + return X; + case TemplateArgument::ArgKind::Template: { + TemplateName TX = X.getAsTemplate(), TY = Y.getAsTemplate(); + TemplateName CTN = ::getCommonTemplateName(Ctx, TX, TY); + if (!CTN.getAsVoidPointer()) + return TemplateArgument(); + return TemplateArgument(CTN); + } + case TemplateArgument::ArgKind::TemplateExpansion: { + TemplateName TX = X.getAsTemplateOrTemplatePattern(), + TY = Y.getAsTemplateOrTemplatePattern(); + TemplateName CTN = ::getCommonTemplateName(Ctx, TX, TY); + if (!CTN.getAsVoidPointer()) + return TemplateName(); + auto NExpX = X.getNumTemplateExpansions(); + assert(NExpX == Y.getNumTemplateExpansions()); + return TemplateArgument(CTN, NExpX); + } + default: + // FIXME: Handle the other argument kinds. + return X; + } +} + +static bool getCommonTemplateArguments(ASTContext &Ctx, + SmallVectorImpl<TemplateArgument> &R, + ArrayRef<TemplateArgument> Xs, + ArrayRef<TemplateArgument> Ys) { + if (Xs.size() != Ys.size()) + return true; + R.resize(Xs.size()); + for (size_t I = 0; I < R.size(); ++I) { + R[I] = getCommonTemplateArgument(Ctx, Xs[I], Ys[I]); + if (R[I].isNull()) + return true; + } + return false; +} + +static auto getCommonTemplateArguments(ASTContext &Ctx, + ArrayRef<TemplateArgument> Xs, + ArrayRef<TemplateArgument> Ys) { + SmallVector<TemplateArgument, 8> R; + bool Different = getCommonTemplateArguments(Ctx, R, Xs, Ys); + assert(!Different); + (void)Different; + return R; +} + +template <class T> +static ElaboratedTypeKeyword getCommonTypeKeyword(const T *X, const T *Y) { + return X->getKeyword() == Y->getKeyword() ? X->getKeyword() + : ElaboratedTypeKeyword::ETK_None; +} + +template <class T> +static NestedNameSpecifier *getCommonNNS(ASTContext &Ctx, const T *X, + const T *Y) { + // FIXME: Try to keep the common NNS sugar. + return X->getQualifier() == Y->getQualifier() + ? X->getQualifier() + : Ctx.getCanonicalNestedNameSpecifier(X->getQualifier()); +} + +template <class T> +static QualType getCommonElementType(ASTContext &Ctx, const T *X, const T *Y) { + return Ctx.getCommonSugaredType(X->getElementType(), Y->getElementType()); +} + +template <class T> +static QualType getCommonArrayElementType(ASTContext &Ctx, const T *X, + Qualifiers &QX, const T *Y, + Qualifiers &QY) { + QualType EX = X->getElementType(), EY = Y->getElementType(); + QualType R = Ctx.getCommonSugaredType(EX, EY, + /*Unqualified=*/true); + Qualifiers RQ = R.getQualifiers(); + QX += EX.getQualifiers() - RQ; + QY += EY.getQualifiers() - RQ; + return R; +} + +template <class T> +static QualType getCommonPointeeType(ASTContext &Ctx, const T *X, const T *Y) { + return Ctx.getCommonSugaredType(X->getPointeeType(), Y->getPointeeType()); +} + +template <class T> static auto *getCommonSizeExpr(ASTContext &Ctx, T *X, T *Y) { + assert(Ctx.hasSameExpr(X->getSizeExpr(), Y->getSizeExpr())); + return X->getSizeExpr(); +} + +static auto getCommonSizeModifier(const ArrayType *X, const ArrayType *Y) { + assert(X->getSizeModifier() == Y->getSizeModifier()); + return X->getSizeModifier(); +} + +static auto getCommonIndexTypeCVRQualifiers(const ArrayType *X, + const ArrayType *Y) { + assert(X->getIndexTypeCVRQualifiers() == Y->getIndexTypeCVRQualifiers()); + return X->getIndexTypeCVRQualifiers(); +} + +// Merges two type lists such that the resulting vector will contain +// each type (in a canonical sense) only once, in the order they appear +// from X to Y. If they occur in both X and Y, the result will contain +// the common sugared type between them. +static void mergeTypeLists(ASTContext &Ctx, SmallVectorImpl<QualType> &Out, + ArrayRef<QualType> X, ArrayRef<QualType> Y) { + llvm::DenseMap<QualType, unsigned> Found; + for (auto Ts : {X, Y}) { + for (QualType T : Ts) { + auto Res = Found.try_emplace(Ctx.getCanonicalType(T), Out.size()); + if (!Res.second) { + QualType &U = Out[Res.first->second]; + U = Ctx.getCommonSugaredType(U, T); + } else { + Out.emplace_back(T); + } + } + } +} + +FunctionProtoType::ExceptionSpecInfo +ASTContext::mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1, + FunctionProtoType::ExceptionSpecInfo ESI2, + SmallVectorImpl<QualType> &ExceptionTypeStorage, + bool AcceptDependent) { + ExceptionSpecificationType EST1 = ESI1.Type, EST2 = ESI2.Type; + + // If either of them can throw anything, that is the result. + for (auto I : {EST_None, EST_MSAny, EST_NoexceptFalse}) { + if (EST1 == I) + return ESI1; + if (EST2 == I) + return ESI2; + } + + // If either of them is non-throwing, the result is the other. + for (auto I : + {EST_NoThrow, EST_DynamicNone, EST_BasicNoexcept, EST_NoexceptTrue}) { + if (EST1 == I) + return ESI2; + if (EST2 == I) + return ESI1; + } + + // If we're left with value-dependent computed noexcept expressions, we're + // stuck. Before C++17, we can just drop the exception specification entirely, + // since it's not actually part of the canonical type. And this should never + // happen in C++17, because it would mean we were computing the composite + // pointer type of dependent types, which should never happen. + if (EST1 == EST_DependentNoexcept || EST2 == EST_DependentNoexcept) { + assert(AcceptDependent && + "computing composite pointer type of dependent types"); + return FunctionProtoType::ExceptionSpecInfo(); + } + + // Switch over the possibilities so that people adding new values know to + // update this function. + switch (EST1) { + case EST_None: + case EST_DynamicNone: + case EST_MSAny: + case EST_BasicNoexcept: + case EST_DependentNoexcept: + case EST_NoexceptFalse: + case EST_NoexceptTrue: + case EST_NoThrow: + llvm_unreachable("These ESTs should be handled above"); + + case EST_Dynamic: { + // This is the fun case: both exception specifications are dynamic. Form + // the union of the two lists. + assert(EST2 == EST_Dynamic && "other cases should already be handled"); + mergeTypeLists(*this, ExceptionTypeStorage, ESI1.Exceptions, + ESI2.Exceptions); + FunctionProtoType::ExceptionSpecInfo Result(EST_Dynamic); + Result.Exceptions = ExceptionTypeStorage; + return Result; + } + + case EST_Unevaluated: + case EST_Uninstantiated: + case EST_Unparsed: + llvm_unreachable("shouldn't see unresolved exception specifications here"); + } + + llvm_unreachable("invalid ExceptionSpecificationType"); +} + +static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, + Qualifiers &QX, const Type *Y, + Qualifiers &QY) { + Type::TypeClass TC = X->getTypeClass(); + assert(TC == Y->getTypeClass()); + switch (TC) { +#define UNEXPECTED_TYPE(Class, Kind) \ + case Type::Class: \ + llvm_unreachable("Unexpected " Kind ": " #Class); + +#define NON_CANONICAL_TYPE(Class, Base) UNEXPECTED_TYPE(Class, "non-canonical") +#define TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + +#define SUGAR_FREE_TYPE(Class) UNEXPECTED_TYPE(Class, "sugar-free") + SUGAR_FREE_TYPE(Builtin) + SUGAR_FREE_TYPE(Decltype) + SUGAR_FREE_TYPE(DeducedTemplateSpecialization) + SUGAR_FREE_TYPE(DependentBitInt) + SUGAR_FREE_TYPE(Enum) + SUGAR_FREE_TYPE(BitInt) + SUGAR_FREE_TYPE(ObjCInterface) + SUGAR_FREE_TYPE(Record) + SUGAR_FREE_TYPE(SubstTemplateTypeParmPack) + SUGAR_FREE_TYPE(UnresolvedUsing) +#undef SUGAR_FREE_TYPE +#define NON_UNIQUE_TYPE(Class) UNEXPECTED_TYPE(Class, "non-unique") + NON_UNIQUE_TYPE(TypeOfExpr) + NON_UNIQUE_TYPE(VariableArray) +#undef NON_UNIQUE_TYPE + + UNEXPECTED_TYPE(TypeOf, "sugar") + +#undef UNEXPECTED_TYPE + + case Type::Auto: { + const auto *AX = cast<AutoType>(X), *AY = cast<AutoType>(Y); + assert(AX->getDeducedType().isNull()); + assert(AY->getDeducedType().isNull()); + assert(AX->getKeyword() == AY->getKeyword()); + assert(AX->isInstantiationDependentType() == + AY->isInstantiationDependentType()); + auto As = getCommonTemplateArguments(Ctx, AX->getTypeConstraintArguments(), + AY->getTypeConstraintArguments()); + return Ctx.getAutoType(QualType(), AX->getKeyword(), + AX->isInstantiationDependentType(), + AX->containsUnexpandedParameterPack(), + getCommonDeclChecked(AX->getTypeConstraintConcept(), + AY->getTypeConstraintConcept()), + As); + } + case Type::IncompleteArray: { + const auto *AX = cast<IncompleteArrayType>(X), + *AY = cast<IncompleteArrayType>(Y); + return Ctx.getIncompleteArrayType( + getCommonArrayElementType(Ctx, AX, QX, AY, QY), + getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY)); + } + case Type::DependentSizedArray: { + const auto *AX = cast<DependentSizedArrayType>(X), + *AY = cast<DependentSizedArrayType>(Y); + return Ctx.getDependentSizedArrayType( + getCommonArrayElementType(Ctx, AX, QX, AY, QY), + getCommonSizeExpr(Ctx, AX, AY), getCommonSizeModifier(AX, AY), + getCommonIndexTypeCVRQualifiers(AX, AY), + AX->getBracketsRange() == AY->getBracketsRange() + ? AX->getBracketsRange() + : SourceRange()); + } + case Type::ConstantArray: { + const auto *AX = cast<ConstantArrayType>(X), + *AY = cast<ConstantArrayType>(Y); + assert(AX->getSize() == AY->getSize()); + const Expr *SizeExpr = Ctx.hasSameExpr(AX->getSizeExpr(), AY->getSizeExpr()) + ? AX->getSizeExpr() + : nullptr; + return Ctx.getConstantArrayType( + getCommonArrayElementType(Ctx, AX, QX, AY, QY), AX->getSize(), SizeExpr, + getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY)); + } + case Type::Atomic: { + const auto *AX = cast<AtomicType>(X), *AY = cast<AtomicType>(Y); + return Ctx.getAtomicType( + Ctx.getCommonSugaredType(AX->getValueType(), AY->getValueType())); + } + case Type::Complex: { + const auto *CX = cast<ComplexType>(X), *CY = cast<ComplexType>(Y); + return Ctx.getComplexType(getCommonArrayElementType(Ctx, CX, QX, CY, QY)); + } + case Type::Pointer: { + const auto *PX = cast<PointerType>(X), *PY = cast<PointerType>(Y); + return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::BlockPointer: { + const auto *PX = cast<BlockPointerType>(X), *PY = cast<BlockPointerType>(Y); + return Ctx.getBlockPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::ObjCObjectPointer: { + const auto *PX = cast<ObjCObjectPointerType>(X), + *PY = cast<ObjCObjectPointerType>(Y); + return Ctx.getObjCObjectPointerType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::MemberPointer: { + const auto *PX = cast<MemberPointerType>(X), + *PY = cast<MemberPointerType>(Y); + return Ctx.getMemberPointerType( + getCommonPointeeType(Ctx, PX, PY), + Ctx.getCommonSugaredType(QualType(PX->getClass(), 0), + QualType(PY->getClass(), 0)) + .getTypePtr()); + } + case Type::LValueReference: { + const auto *PX = cast<LValueReferenceType>(X), + *PY = cast<LValueReferenceType>(Y); + // FIXME: Preserve PointeeTypeAsWritten. + return Ctx.getLValueReferenceType(getCommonPointeeType(Ctx, PX, PY), + PX->isSpelledAsLValue() || + PY->isSpelledAsLValue()); + } + case Type::RValueReference: { + const auto *PX = cast<RValueReferenceType>(X), + *PY = cast<RValueReferenceType>(Y); + // FIXME: Preserve PointeeTypeAsWritten. + return Ctx.getRValueReferenceType(getCommonPointeeType(Ctx, PX, PY)); + } + case Type::DependentAddressSpace: { + const auto *PX = cast<DependentAddressSpaceType>(X), + *PY = cast<DependentAddressSpaceType>(Y); + assert(Ctx.hasSameExpr(PX->getAddrSpaceExpr(), PY->getAddrSpaceExpr())); + return Ctx.getDependentAddressSpaceType(getCommonPointeeType(Ctx, PX, PY), + PX->getAddrSpaceExpr(), + getCommonAttrLoc(PX, PY)); + } + case Type::FunctionNoProto: { + const auto *FX = cast<FunctionNoProtoType>(X), + *FY = cast<FunctionNoProtoType>(Y); + assert(FX->getExtInfo() == FY->getExtInfo()); + return Ctx.getFunctionNoProtoType( + Ctx.getCommonSugaredType(FX->getReturnType(), FY->getReturnType()), + FX->getExtInfo()); + } + case Type::FunctionProto: { + const auto *FX = cast<FunctionProtoType>(X), + *FY = cast<FunctionProtoType>(Y); + FunctionProtoType::ExtProtoInfo EPIX = FX->getExtProtoInfo(), + EPIY = FY->getExtProtoInfo(); + assert(EPIX.ExtInfo == EPIY.ExtInfo); + assert(EPIX.ExtParameterInfos == EPIY.ExtParameterInfos); + assert(EPIX.RefQualifier == EPIY.RefQualifier); + assert(EPIX.TypeQuals == EPIY.TypeQuals); + assert(EPIX.Variadic == EPIY.Variadic); + + // FIXME: Can we handle an empty EllipsisLoc? + // Use emtpy EllipsisLoc if X and Y differ. + + EPIX.HasTrailingReturn = EPIX.HasTrailingReturn && EPIY.HasTrailingReturn; + + QualType R = + Ctx.getCommonSugaredType(FX->getReturnType(), FY->getReturnType()); + auto P = getCommonTypes(Ctx, FX->param_types(), FY->param_types(), + /*Unqualified=*/true); + + SmallVector<QualType, 8> Exceptions; + EPIX.ExceptionSpec = Ctx.mergeExceptionSpecs( + EPIX.ExceptionSpec, EPIY.ExceptionSpec, Exceptions, true); + return Ctx.getFunctionType(R, P, EPIX); + } + case Type::ObjCObject: { + const auto *OX = cast<ObjCObjectType>(X), *OY = cast<ObjCObjectType>(Y); + assert( + std::equal(OX->getProtocols().begin(), OX->getProtocols().end(), + OY->getProtocols().begin(), OY->getProtocols().end(), + [](const ObjCProtocolDecl *P0, const ObjCProtocolDecl *P1) { + return P0->getCanonicalDecl() == P1->getCanonicalDecl(); + }) && + "protocol lists must be the same"); + auto TAs = getCommonTypes(Ctx, OX->getTypeArgsAsWritten(), + OY->getTypeArgsAsWritten()); + return Ctx.getObjCObjectType( + Ctx.getCommonSugaredType(OX->getBaseType(), OY->getBaseType()), TAs, + OX->getProtocols(), + OX->isKindOfTypeAsWritten() && OY->isKindOfTypeAsWritten()); + } + case Type::ConstantMatrix: { + const auto *MX = cast<ConstantMatrixType>(X), + *MY = cast<ConstantMatrixType>(Y); + assert(MX->getNumRows() == MY->getNumRows()); + assert(MX->getNumColumns() == MY->getNumColumns()); + return Ctx.getConstantMatrixType(getCommonElementType(Ctx, MX, MY), + MX->getNumRows(), MX->getNumColumns()); + } + case Type::DependentSizedMatrix: { + const auto *MX = cast<DependentSizedMatrixType>(X), + *MY = cast<DependentSizedMatrixType>(Y); + assert(Ctx.hasSameExpr(MX->getRowExpr(), MY->getRowExpr())); + assert(Ctx.hasSameExpr(MX->getColumnExpr(), MY->getColumnExpr())); + return Ctx.getDependentSizedMatrixType( + getCommonElementType(Ctx, MX, MY), MX->getRowExpr(), + MX->getColumnExpr(), getCommonAttrLoc(MX, MY)); + } + case Type::Vector: { + const auto *VX = cast<VectorType>(X), *VY = cast<VectorType>(Y); + assert(VX->getNumElements() == VY->getNumElements()); + assert(VX->getVectorKind() == VY->getVectorKind()); + return Ctx.getVectorType(getCommonElementType(Ctx, VX, VY), + VX->getNumElements(), VX->getVectorKind()); + } + case Type::ExtVector: { + const auto *VX = cast<ExtVectorType>(X), *VY = cast<ExtVectorType>(Y); + assert(VX->getNumElements() == VY->getNumElements()); + return Ctx.getExtVectorType(getCommonElementType(Ctx, VX, VY), + VX->getNumElements()); + } + case Type::DependentSizedExtVector: { + const auto *VX = cast<DependentSizedExtVectorType>(X), + *VY = cast<DependentSizedExtVectorType>(Y); + return Ctx.getDependentSizedExtVectorType(getCommonElementType(Ctx, VX, VY), + getCommonSizeExpr(Ctx, VX, VY), + getCommonAttrLoc(VX, VY)); + } + case Type::DependentVector: { + const auto *VX = cast<DependentVectorType>(X), + *VY = cast<DependentVectorType>(Y); + assert(VX->getVectorKind() == VY->getVectorKind()); + return Ctx.getDependentVectorType( + getCommonElementType(Ctx, VX, VY), getCommonSizeExpr(Ctx, VX, VY), + getCommonAttrLoc(VX, VY), VX->getVectorKind()); + } + case Type::InjectedClassName: { + const auto *IX = cast<InjectedClassNameType>(X), + *IY = cast<InjectedClassNameType>(Y); + return Ctx.getInjectedClassNameType( + getCommonDeclChecked(IX->getDecl(), IY->getDecl()), + Ctx.getCommonSugaredType(IX->getInjectedSpecializationType(), + IY->getInjectedSpecializationType())); + } + case Type::TemplateSpecialization: { + const auto *TX = cast<TemplateSpecializationType>(X), + *TY = cast<TemplateSpecializationType>(Y); + auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(), + TY->template_arguments()); + return Ctx.getTemplateSpecializationType( + ::getCommonTemplateNameChecked(Ctx, TX->getTemplateName(), + TY->getTemplateName()), + As, X->getCanonicalTypeInternal()); + } + case Type::DependentName: { + const auto *NX = cast<DependentNameType>(X), + *NY = cast<DependentNameType>(Y); + assert(NX->getIdentifier() == NY->getIdentifier()); + return Ctx.getDependentNameType( + getCommonTypeKeyword(NX, NY), getCommonNNS(Ctx, NX, NY), + NX->getIdentifier(), NX->getCanonicalTypeInternal()); + } + case Type::DependentTemplateSpecialization: { + const auto *TX = cast<DependentTemplateSpecializationType>(X), + *TY = cast<DependentTemplateSpecializationType>(Y); + assert(TX->getIdentifier() == TY->getIdentifier()); + auto As = getCommonTemplateArguments(Ctx, TX->template_arguments(), + TY->template_arguments()); + return Ctx.getDependentTemplateSpecializationType( + getCommonTypeKeyword(TX, TY), getCommonNNS(Ctx, TX, TY), + TX->getIdentifier(), As); + } + case Type::UnaryTransform: { + const auto *TX = cast<UnaryTransformType>(X), + *TY = cast<UnaryTransformType>(Y); + assert(TX->getUTTKind() == TY->getUTTKind()); + return Ctx.getUnaryTransformType( + Ctx.getCommonSugaredType(TX->getBaseType(), TY->getBaseType()), + Ctx.getCommonSugaredType(TX->getUnderlyingType(), + TY->getUnderlyingType()), + TX->getUTTKind()); + } + case Type::PackExpansion: { + const auto *PX = cast<PackExpansionType>(X), + *PY = cast<PackExpansionType>(Y); + assert(PX->getNumExpansions() == PY->getNumExpansions()); + return Ctx.getPackExpansionType( + Ctx.getCommonSugaredType(PX->getPattern(), PY->getPattern()), + PX->getNumExpansions(), false); + } + case Type::Pipe: { + const auto *PX = cast<PipeType>(X), *PY = cast<PipeType>(Y); + assert(PX->isReadOnly() == PY->isReadOnly()); + auto MP = PX->isReadOnly() ? &ASTContext::getReadPipeType + : &ASTContext::getWritePipeType; + return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY)); + } + case Type::TemplateTypeParm: { + const auto *TX = cast<TemplateTypeParmType>(X), + *TY = cast<TemplateTypeParmType>(Y); + assert(TX->getDepth() == TY->getDepth()); + assert(TX->getIndex() == TY->getIndex()); + assert(TX->isParameterPack() == TY->isParameterPack()); + return Ctx.getTemplateTypeParmType( + TX->getDepth(), TX->getIndex(), TX->isParameterPack(), + getCommonDecl(TX->getDecl(), TY->getDecl())); + } + } + llvm_unreachable("Unknown Type Class"); +} + +static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, + const Type *Y, + SplitQualType Underlying) { + Type::TypeClass TC = X->getTypeClass(); + if (TC != Y->getTypeClass()) + return QualType(); + switch (TC) { +#define UNEXPECTED_TYPE(Class, Kind) \ + case Type::Class: \ + llvm_unreachable("Unexpected " Kind ": " #Class); +#define TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) UNEXPECTED_TYPE(Class, "dependent") +#include "clang/AST/TypeNodes.inc" + +#define CANONICAL_TYPE(Class) UNEXPECTED_TYPE(Class, "canonical") + CANONICAL_TYPE(Atomic) + CANONICAL_TYPE(BitInt) + CANONICAL_TYPE(BlockPointer) + CANONICAL_TYPE(Builtin) + CANONICAL_TYPE(Complex) + CANONICAL_TYPE(ConstantArray) + CANONICAL_TYPE(ConstantMatrix) + CANONICAL_TYPE(Enum) + CANONICAL_TYPE(ExtVector) + CANONICAL_TYPE(FunctionNoProto) + CANONICAL_TYPE(FunctionProto) + CANONICAL_TYPE(IncompleteArray) + CANONICAL_TYPE(LValueReference) + CANONICAL_TYPE(MemberPointer) + CANONICAL_TYPE(ObjCInterface) + CANONICAL_TYPE(ObjCObject) + CANONICAL_TYPE(ObjCObjectPointer) + CANONICAL_TYPE(Pipe) + CANONICAL_TYPE(Pointer) + CANONICAL_TYPE(Record) + CANONICAL_TYPE(RValueReference) + CANONICAL_TYPE(VariableArray) + CANONICAL_TYPE(Vector) +#undef CANONICAL_TYPE + +#undef UNEXPECTED_TYPE + + case Type::Adjusted: { + const auto *AX = cast<AdjustedType>(X), *AY = cast<AdjustedType>(Y); + QualType OX = AX->getOriginalType(), OY = AY->getOriginalType(); + if (!Ctx.hasSameType(OX, OY)) + return QualType(); + // FIXME: It's inefficient to have to unify the original types. + return Ctx.getAdjustedType(Ctx.getCommonSugaredType(OX, OY), + Ctx.getQualifiedType(Underlying)); + } + case Type::Decayed: { + const auto *DX = cast<DecayedType>(X), *DY = cast<DecayedType>(Y); + QualType OX = DX->getOriginalType(), OY = DY->getOriginalType(); + if (!Ctx.hasSameType(OX, OY)) + return QualType(); + // FIXME: It's inefficient to have to unify the original types. + return Ctx.getDecayedType(Ctx.getCommonSugaredType(OX, OY), + Ctx.getQualifiedType(Underlying)); + } + case Type::Attributed: { + const auto *AX = cast<AttributedType>(X), *AY = cast<AttributedType>(Y); + AttributedType::Kind Kind = AX->getAttrKind(); + if (Kind != AY->getAttrKind()) + return QualType(); + QualType MX = AX->getModifiedType(), MY = AY->getModifiedType(); + if (!Ctx.hasSameType(MX, MY)) + return QualType(); + // FIXME: It's inefficient to have to unify the modified types. + return Ctx.getAttributedType(Kind, Ctx.getCommonSugaredType(MX, MY), + Ctx.getQualifiedType(Underlying)); + } + case Type::BTFTagAttributed: { + const auto *BX = cast<BTFTagAttributedType>(X); + const BTFTypeTagAttr *AX = BX->getAttr(); + // The attribute is not uniqued, so just compare the tag. + if (AX->getBTFTypeTag() != + cast<BTFTagAttributedType>(Y)->getAttr()->getBTFTypeTag()) + return QualType(); + return Ctx.getBTFTagAttributedType(AX, Ctx.getQualifiedType(Underlying)); + } + case Type::Auto: { + const auto *AX = cast<AutoType>(X), *AY = cast<AutoType>(Y); + + AutoTypeKeyword KW = AX->getKeyword(); + if (KW != AY->getKeyword()) + return QualType(); + + ConceptDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(), + AY->getTypeConstraintConcept()); + SmallVector<TemplateArgument, 8> As; + if (CD && + getCommonTemplateArguments(Ctx, As, AX->getTypeConstraintArguments(), + AY->getTypeConstraintArguments())) + CD = nullptr; // The arguments differ, so make it unconstrained. + + // Both auto types can't be dependent, otherwise they wouldn't have been + // sugar. This implies they can't contain unexpanded packs either. + return Ctx.getAutoType(Ctx.getQualifiedType(Underlying), AX->getKeyword(), + /*IsDependent=*/false, /*IsPack=*/false, CD, As); + } + case Type::Decltype: + return QualType(); + case Type::DeducedTemplateSpecialization: + // FIXME: Try to merge these. + return QualType(); + + case Type::Elaborated: { + const auto *EX = cast<ElaboratedType>(X), *EY = cast<ElaboratedType>(Y); + return Ctx.getElaboratedType( + ::getCommonTypeKeyword(EX, EY), ::getCommonNNS(Ctx, EX, EY), + Ctx.getQualifiedType(Underlying), + ::getCommonDecl(EX->getOwnedTagDecl(), EY->getOwnedTagDecl())); + } + case Type::MacroQualified: { + const auto *MX = cast<MacroQualifiedType>(X), + *MY = cast<MacroQualifiedType>(Y); + const IdentifierInfo *IX = MX->getMacroIdentifier(); + if (IX != MY->getMacroIdentifier()) + return QualType(); + return Ctx.getMacroQualifiedType(Ctx.getQualifiedType(Underlying), IX); + } + case Type::SubstTemplateTypeParm: { + const auto *SX = cast<SubstTemplateTypeParmType>(X), + *SY = cast<SubstTemplateTypeParmType>(Y); + Decl *CD = + ::getCommonDecl(SX->getAssociatedDecl(), SY->getAssociatedDecl()); + if (!CD) + return QualType(); + unsigned Index = SX->getIndex(); + if (Index != SY->getIndex()) + return QualType(); + auto PackIndex = SX->getPackIndex(); + if (PackIndex != SY->getPackIndex()) + return QualType(); + return Ctx.getSubstTemplateTypeParmType(Ctx.getQualifiedType(Underlying), + CD, Index, PackIndex); + } + case Type::ObjCTypeParam: + // FIXME: Try to merge these. + return QualType(); + case Type::Paren: + return Ctx.getParenType(Ctx.getQualifiedType(Underlying)); + + case Type::TemplateSpecialization: { + const auto *TX = cast<TemplateSpecializationType>(X), + *TY = cast<TemplateSpecializationType>(Y); + TemplateName CTN = ::getCommonTemplateName(Ctx, TX->getTemplateName(), + TY->getTemplateName()); + if (!CTN.getAsVoidPointer()) + return QualType(); + SmallVector<TemplateArgument, 8> Args; + if (getCommonTemplateArguments(Ctx, Args, TX->template_arguments(), + TY->template_arguments())) + return QualType(); + return Ctx.getTemplateSpecializationType(CTN, Args, + Ctx.getQualifiedType(Underlying)); + } + case Type::Typedef: { + const auto *TX = cast<TypedefType>(X), *TY = cast<TypedefType>(Y); + const TypedefNameDecl *CD = ::getCommonDecl(TX->getDecl(), TY->getDecl()); + if (!CD) + return QualType(); + return Ctx.getTypedefType(CD, Ctx.getQualifiedType(Underlying)); + } + case Type::TypeOf: { + // The common sugar between two typeof expressions, where one is + // potentially a typeof_unqual and the other is not, we unify to the + // qualified type as that retains the most information along with the type. + // We only return a typeof_unqual type when both types are unqual types. + TypeOfKind Kind = TypeOfKind::Qualified; + if (cast<TypeOfType>(X)->getKind() == cast<TypeOfType>(Y)->getKind() && + cast<TypeOfType>(X)->getKind() == TypeOfKind::Unqualified) + Kind = TypeOfKind::Unqualified; + return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying), Kind); + } + case Type::TypeOfExpr: + return QualType(); + + case Type::UnaryTransform: { + const auto *UX = cast<UnaryTransformType>(X), + *UY = cast<UnaryTransformType>(Y); + UnaryTransformType::UTTKind KX = UX->getUTTKind(); + if (KX != UY->getUTTKind()) + return QualType(); + QualType BX = UX->getBaseType(), BY = UY->getBaseType(); + if (!Ctx.hasSameType(BX, BY)) + return QualType(); + // FIXME: It's inefficient to have to unify the base types. + return Ctx.getUnaryTransformType(Ctx.getCommonSugaredType(BX, BY), + Ctx.getQualifiedType(Underlying), KX); + } + case Type::Using: { + const auto *UX = cast<UsingType>(X), *UY = cast<UsingType>(Y); + const UsingShadowDecl *CD = + ::getCommonDecl(UX->getFoundDecl(), UY->getFoundDecl()); + if (!CD) + return QualType(); + return Ctx.getUsingType(CD, Ctx.getQualifiedType(Underlying)); + } + } + llvm_unreachable("Unhandled Type Class"); +} + +static auto unwrapSugar(SplitQualType &T, Qualifiers &QTotal) { + SmallVector<SplitQualType, 8> R; + while (true) { + QTotal += T.Quals; + QualType NT = T.Ty->getLocallyUnqualifiedSingleStepDesugaredType(); + if (NT == QualType(T.Ty, 0)) + break; + R.push_back(T); + T = NT.split(); + } + return R; +} + +QualType ASTContext::getCommonSugaredType(QualType X, QualType Y, + bool Unqualified) { + assert(Unqualified ? hasSameUnqualifiedType(X, Y) : hasSameType(X, Y)); + if (X == Y) + return X; + if (!Unqualified) { + if (X.isCanonical()) + return X; + if (Y.isCanonical()) + return Y; + } + + SplitQualType SX = X.split(), SY = Y.split(); + Qualifiers QX, QY; + // Desugar SX and SY, setting the sugar and qualifiers aside into Xs and Ys, + // until we reach their underlying "canonical nodes". Note these are not + // necessarily canonical types, as they may still have sugared properties. + // QX and QY will store the sum of all qualifiers in Xs and Ys respectively. + auto Xs = ::unwrapSugar(SX, QX), Ys = ::unwrapSugar(SY, QY); + if (SX.Ty != SY.Ty) { + // The canonical nodes differ. Build a common canonical node out of the two, + // unifying their sugar. This may recurse back here. + SX.Ty = + ::getCommonNonSugarTypeNode(*this, SX.Ty, QX, SY.Ty, QY).getTypePtr(); + } else { + // The canonical nodes were identical: We may have desugared too much. + // Add any common sugar back in. + while (!Xs.empty() && !Ys.empty() && Xs.back().Ty == Ys.back().Ty) { + QX -= SX.Quals; + QY -= SY.Quals; + SX = Xs.pop_back_val(); + SY = Ys.pop_back_val(); + } + } + if (Unqualified) + QX = Qualifiers::removeCommonQualifiers(QX, QY); else - return (*AddrSpaceMap)[(unsigned)AS]; + assert(QX == QY); + + // Even though the remaining sugar nodes in Xs and Ys differ, some may be + // related. Walk up these nodes, unifying them and adding the result. + while (!Xs.empty() && !Ys.empty()) { + auto Underlying = SplitQualType( + SX.Ty, Qualifiers::removeCommonQualifiers(SX.Quals, SY.Quals)); + SX = Xs.pop_back_val(); + SY = Ys.pop_back_val(); + SX.Ty = ::getCommonSugarTypeNode(*this, SX.Ty, SY.Ty, Underlying) + .getTypePtrOrNull(); + // Stop at the first pair which is unrelated. + if (!SX.Ty) { + SX.Ty = Underlying.Ty; + break; + } + QX -= Underlying.Quals; + }; + + // Add back the missing accumulated qualifiers, which were stripped off + // with the sugar nodes we could not unify. + QualType R = getQualifiedType(SX.Ty, QX); + assert(Unqualified ? hasSameUnqualifiedType(R, X) : hasSameType(R, X)); + return R; } QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const { @@ -12304,10 +13308,22 @@ QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const { } } +std::vector<std::string> ASTContext::filterFunctionTargetVersionAttrs( + const TargetVersionAttr *TV) const { + assert(TV != nullptr); + llvm::SmallVector<StringRef, 8> Feats; + std::vector<std::string> ResFeats; + TV->getFeatures(Feats); + for (auto &Feature : Feats) + if (Target->validateCpuSupports(Feature.str())) + ResFeats.push_back("?" + Feature.str()); + return ResFeats; +} + ParsedTargetAttr ASTContext::filterFunctionTargetAttrs(const TargetAttr *TD) const { assert(TD != nullptr); - ParsedTargetAttr ParsedAttr = TD->parse(); + ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(TD->getFeaturesStr()); llvm::erase_if(ParsedAttr.Features, [&](const std::string &Feat) { return !Target->isValidFeatureName(StringRef{Feat}.substr(1)); @@ -12341,9 +13357,8 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap, Target->getTargetOpts().FeaturesAsWritten.begin(), Target->getTargetOpts().FeaturesAsWritten.end()); - if (ParsedAttr.Architecture != "" && - Target->isValidCPUName(ParsedAttr.Architecture)) - TargetCPU = ParsedAttr.Architecture; + if (ParsedAttr.CPU != "" && Target->isValidCPUName(ParsedAttr.CPU)) + TargetCPU = ParsedAttr.CPU; // Now populate the feature map, first with the TargetCPU which is either // the default or a new one from the target attribute string. Then we'll use @@ -12363,12 +13378,32 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap, } else if (const auto *TC = FD->getAttr<TargetClonesAttr>()) { std::vector<std::string> Features; StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex()); - if (VersionStr.startswith("arch=")) - TargetCPU = VersionStr.drop_front(sizeof("arch=") - 1); - else if (VersionStr != "default") - Features.push_back((StringRef{"+"} + VersionStr).str()); - + if (Target->getTriple().isAArch64()) { + // TargetClones for AArch64 + if (VersionStr != "default") { + SmallVector<StringRef, 1> VersionFeatures; + VersionStr.split(VersionFeatures, "+"); + for (auto &VFeature : VersionFeatures) { + VFeature = VFeature.trim(); + Features.push_back((StringRef{"?"} + VFeature).str()); + } + } + Features.insert(Features.begin(), + Target->getTargetOpts().FeaturesAsWritten.begin(), + Target->getTargetOpts().FeaturesAsWritten.end()); + } else { + if (VersionStr.startswith("arch=")) + TargetCPU = VersionStr.drop_front(sizeof("arch=") - 1); + else if (VersionStr != "default") + Features.push_back((StringRef{"+"} + VersionStr).str()); + } Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features); + } else if (const auto *TV = FD->getAttr<TargetVersionAttr>()) { + std::vector<std::string> Feats = filterFunctionTargetVersionAttrs(TV); + Feats.insert(Feats.begin(), + Target->getTargetOpts().FeaturesAsWritten.begin(), + Target->getTargetOpts().FeaturesAsWritten.end()); + Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Feats); } else { FeatureMap = Target->getTargetOpts().FeatureMap; } diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 28269ec219e4..08877aa12c02 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -118,8 +118,7 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT, if (!TST->isTypeAlias()) { bool DesugarArgument = false; SmallVector<TemplateArgument, 4> Args; - for (unsigned I = 0, N = TST->getNumArgs(); I != N; ++I) { - const TemplateArgument &Arg = TST->getArg(I); + for (const TemplateArgument &Arg : TST->template_arguments()) { if (Arg.getKind() == TemplateArgument::Type) Args.push_back(desugarForDiagnostic(Context, Arg.getAsType(), DesugarArgument)); @@ -228,7 +227,7 @@ break; \ desugarForDiagnostic(Context, Ty->getBaseType(), ShouldAKA); QT = Context.getObjCObjectType( BaseType, Ty->getTypeArgsAsWritten(), - llvm::makeArrayRef(Ty->qual_begin(), Ty->getNumProtocols()), + llvm::ArrayRef(Ty->qual_begin(), Ty->getNumProtocols()), Ty->isKindOfTypeAsWritten()); } } @@ -425,7 +424,7 @@ void clang::FormatASTNodeDiagnosticArgument( Modifier = StringRef(); Argument = StringRef(); // Fall through - LLVM_FALLTHROUGH; + [[fallthrough]]; } case DiagnosticsEngine::ak_qualtype: { assert(Modifier.empty() && Argument.empty() && @@ -539,7 +538,7 @@ class TemplateDiff { bool ShowColor; /// FromTemplateType - When single type printing is selected, this is the - /// type to be be printed. When tree printing is selected, this type will + /// type to be printed. When tree printing is selected, this type will /// show up first in the tree. QualType FromTemplateType; @@ -986,7 +985,7 @@ class TemplateDiff { if (isEnd()) return; // Set to first template argument. If not a parameter pack, done. - TemplateArgument TA = TST->getArg(0); + TemplateArgument TA = TST->template_arguments()[0]; if (TA.getKind() != TemplateArgument::Pack) return; // Start looking into the parameter pack. @@ -1007,7 +1006,7 @@ class TemplateDiff { /// isEnd - Returns true if the iterator is one past the end. bool isEnd() const { assert(TST && "InternalIterator is invalid with a null TST."); - return Index >= TST->getNumArgs(); + return Index >= TST->template_arguments().size(); } /// &operator++ - Increment the iterator to the next template argument. @@ -1027,11 +1026,11 @@ class TemplateDiff { // Loop until a template argument is found, or the end is reached. while (true) { // Advance to the next template argument. Break if reached the end. - if (++Index == TST->getNumArgs()) + if (++Index == TST->template_arguments().size()) break; // If the TemplateArgument is not a parameter pack, done. - TemplateArgument TA = TST->getArg(Index); + TemplateArgument TA = TST->template_arguments()[Index]; if (TA.getKind() != TemplateArgument::Pack) break; @@ -1051,7 +1050,7 @@ class TemplateDiff { assert(TST && "InternalIterator is invalid with a null TST."); assert(!isEnd() && "Index exceeds number of arguments."); if (CurrentTA == EndTA) - return TST->getArg(Index); + return TST->template_arguments()[Index]; else return *CurrentTA; } @@ -1684,9 +1683,24 @@ class TemplateDiff { : FromType.getAsString(Policy); std::string ToTypeStr = ToType.isNull() ? "(no argument)" : ToType.getAsString(Policy); - // Switch to canonical typename if it is better. + // Print without ElaboratedType sugar if it is better. // TODO: merge this with other aka printing above. if (FromTypeStr == ToTypeStr) { + const auto *FromElTy = dyn_cast<ElaboratedType>(FromType), + *ToElTy = dyn_cast<ElaboratedType>(ToType); + if (FromElTy || ToElTy) { + std::string FromNamedTypeStr = + FromElTy ? FromElTy->getNamedType().getAsString(Policy) + : FromTypeStr; + std::string ToNamedTypeStr = + ToElTy ? ToElTy->getNamedType().getAsString(Policy) : ToTypeStr; + if (FromNamedTypeStr != ToNamedTypeStr) { + FromTypeStr = FromNamedTypeStr; + ToTypeStr = ToNamedTypeStr; + goto PrintTypes; + } + } + // Switch to canonical typename if it is better. std::string FromCanTypeStr = FromType.getCanonicalType().getAsString(Policy); std::string ToCanTypeStr = ToType.getCanonicalType().getAsString(Policy); @@ -1696,6 +1710,7 @@ class TemplateDiff { } } + PrintTypes: if (PrintTree) OS << '['; OS << (FromDefault ? "(default) " : ""); Bold(); @@ -1877,7 +1892,7 @@ class TemplateDiff { TPO->printAsInit(OS, Policy); return; } - VD->printName(OS); + VD->printName(OS, Policy); return; } diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index c6df61f79e2e..9900efb5a48d 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -19,9 +19,37 @@ #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/raw_ostream.h" + using namespace clang; using namespace clang::comments; +void ASTDumper::dumpInvalidDeclContext(const DeclContext *DC) { + NodeDumper.AddChild([=] { + if (!DC) { + ColorScope Color(OS, ShowColors, NullColor); + OS << "<<<NULL>>>"; + return; + } + // An invalid DeclContext is one for which a dyn_cast() from a DeclContext + // pointer to a Decl pointer would fail an assertion or otherwise fall prey + // to undefined behavior as a result of an invalid associated DeclKind. + // Such invalidity is not supposed to happen of course, but, when it does, + // the information provided below is intended to provide some hints about + // what might have gone awry. + { + ColorScope Color(OS, ShowColors, DeclKindNameColor); + OS << "DeclContext"; + } + NodeDumper.dumpPointer(DC); + OS << " <"; + { + ColorScope Color(OS, ShowColors, DeclNameColor); + OS << "unrecognized Decl kind " << (unsigned)DC->getDeclKind(); + } + OS << ">"; + }); +} + void ASTDumper::dumpLookups(const DeclContext *DC, bool DumpDecls) { NodeDumper.AddChild([=] { OS << "StoredDeclsMap "; @@ -96,7 +124,7 @@ void ASTDumper::dumpTemplateDeclSpecialization(const SpecializationDecl *D, case TSK_ExplicitInstantiationDefinition: if (!DumpExplicitInst) break; - LLVM_FALLTHROUGH; + [[fallthrough]]; case TSK_Undeclared: case TSK_ImplicitInstantiation: if (DumpRefOnly) @@ -200,6 +228,31 @@ LLVM_DUMP_METHOD void Decl::dumpColor() const { P.Visit(this); } +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl() const { + dumpAsDecl(nullptr); +} + +LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const { + // By design, DeclContext is required to be a base class of some class that + // derives from Decl. Thus, it should always be possible to dyn_cast() from + // a DeclContext pointer to a Decl pointer and Decl::castFromDeclContext() + // asserts that to be the case. Since this function is intended for use in a + // debugger, it performs an additional check in order to prevent a failed + // cast and assertion. If that check fails, then the (invalid) DeclContext + // is dumped with an indication of its invalidity. + if (hasValidDeclKind()) { + const auto *D = cast<Decl>(this); + D->dump(); + } else { + // If an ASTContext is not available, a less capable ASTDumper is + // constructed for which color diagnostics are, regrettably, disabled. + ASTDumper P = Ctx ? ASTDumper(llvm::errs(), *Ctx, + Ctx->getDiagnostics().getShowColors()) + : ASTDumper(llvm::errs(), /*ShowColors*/ false); + P.dumpInvalidDeclContext(this); + } +} + LLVM_DUMP_METHOD void DeclContext::dumpLookups() const { dumpLookups(llvm::errs()); } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 0273e5068371..6f367ef053d2 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -34,7 +34,6 @@ #include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.h" -#include "clang/AST/ParentMapContext.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -57,8 +56,6 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" @@ -69,6 +66,7 @@ #include <cassert> #include <cstddef> #include <memory> +#include <optional> #include <type_traits> #include <utility> @@ -184,13 +182,13 @@ namespace clang { // Use this instead of Importer.importInto . template <typename ImportT> - LLVM_NODISCARD Error importInto(ImportT &To, const ImportT &From) { + [[nodiscard]] Error importInto(ImportT &To, const ImportT &From) { return Importer.importInto(To, From); } // Use this to import pointers of specific type. template <typename ImportT> - LLVM_NODISCARD Error importInto(ImportT *&To, ImportT *From) { + [[nodiscard]] Error importInto(ImportT *&To, ImportT *From) { auto ToOrErr = Importer.Import(From); if (ToOrErr) To = cast_or_null<ImportT>(*ToOrErr); @@ -201,8 +199,8 @@ namespace clang { // cast the return value to `T`. template <typename T> auto import(T *From) - -> std::conditional_t<std::is_base_of<Type, T>::value, - Expected<const T *>, Expected<T *>> { + -> std::conditional_t<std::is_base_of_v<Type, T>, Expected<const T *>, + Expected<T *>> { auto ToOrErr = Importer.Import(From); if (!ToOrErr) return ToOrErr.takeError(); @@ -220,11 +218,11 @@ namespace clang { return Importer.Import(From); } - // Import an Optional<T> by importing the contained T, if any. - template<typename T> - Expected<Optional<T>> import(Optional<T> From) { + // Import an std::optional<T> by importing the contained T, if any. + template <typename T> + Expected<std::optional<T>> import(std::optional<T> From) { if (!From) - return Optional<T>(); + return std::nullopt; return import(*From); } @@ -245,8 +243,8 @@ namespace clang { // then to the already imported Decl. Returns a bool value set to true if // the `FromD` had been imported before. template <typename ToDeclT, typename FromDeclT, typename... Args> - LLVM_NODISCARD bool GetImportedOrCreateDecl(ToDeclT *&ToD, FromDeclT *FromD, - Args &&... args) { + [[nodiscard]] bool GetImportedOrCreateDecl(ToDeclT *&ToD, FromDeclT *FromD, + Args &&...args) { // There may be several overloads of ToDeclT::Create. We must make sure // to call the one which would be chosen by the arguments, thus we use a // wrapper for the overload set. @@ -261,8 +259,8 @@ namespace clang { // GetImportedOrCreateDecl<TypeAliasDecl>(ToTypedef, FromD, ...); template <typename NewDeclT, typename ToDeclT, typename FromDeclT, typename... Args> - LLVM_NODISCARD bool GetImportedOrCreateDecl(ToDeclT *&ToD, FromDeclT *FromD, - Args &&... args) { + [[nodiscard]] bool GetImportedOrCreateDecl(ToDeclT *&ToD, FromDeclT *FromD, + Args &&...args) { CallOverloadedCreateFun<NewDeclT> OC; return GetImportedOrCreateSpecialDecl(ToD, OC, FromD, std::forward<Args>(args)...); @@ -271,9 +269,9 @@ namespace clang { // used, e.g. CXXRecordDecl::CreateLambda . template <typename ToDeclT, typename CreateFunT, typename FromDeclT, typename... Args> - LLVM_NODISCARD bool + [[nodiscard]] bool GetImportedOrCreateSpecialDecl(ToDeclT *&ToD, CreateFunT CreateFun, - FromDeclT *FromD, Args &&... args) { + FromDeclT *FromD, Args &&...args) { if (Importer.getImportDeclErrorIfAny(FromD)) { ToD = nullptr; return true; // Already imported but with error. @@ -471,9 +469,8 @@ namespace clang { Error ImportDefinition( ObjCProtocolDecl *From, ObjCProtocolDecl *To, ImportDefinitionKind Kind = IDK_Default); - Error ImportTemplateArguments( - const TemplateArgument *FromArgs, unsigned NumFromArgs, - SmallVectorImpl<TemplateArgument> &ToArgs); + Error ImportTemplateArguments(ArrayRef<TemplateArgument> FromArgs, + SmallVectorImpl<TemplateArgument> &ToArgs); Expected<TemplateArgument> ImportTemplateArgument(const TemplateArgument &From); @@ -791,9 +788,8 @@ ASTNodeImporter::ImportFunctionTemplateWithTemplateArgsFromSpecialization( return std::move(Err); // Import template arguments. - auto TemplArgs = FTSInfo->TemplateArguments->asArray(); - if (Error Err = ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(), - std::get<1>(Result))) + if (Error Err = ImportTemplateArguments(FTSInfo->TemplateArguments->asArray(), + std::get<1>(Result))) return std::move(Err); return Result; @@ -894,12 +890,11 @@ ASTNodeImporter::import(const TemplateArgument &From) { case TemplateArgument::Pack: { SmallVector<TemplateArgument, 2> ToPack; ToPack.reserve(From.pack_size()); - if (Error Err = ImportTemplateArguments( - From.pack_begin(), From.pack_size(), ToPack)) + if (Error Err = ImportTemplateArguments(From.pack_elements(), ToPack)) return std::move(Err); return TemplateArgument( - llvm::makeArrayRef(ToPack).copy(Importer.getToContext())); + llvm::ArrayRef(ToPack).copy(Importer.getToContext())); } } @@ -1006,7 +1001,7 @@ ASTNodeImporter::import(const Designator &D) { template <> Expected<LambdaCapture> ASTNodeImporter::import(const LambdaCapture &From) { - VarDecl *Var = nullptr; + ValueDecl *Var = nullptr; if (From.capturesVariable()) { if (auto VarOrErr = import(From.getCapturedVar())) Var = *VarOrErr; @@ -1362,24 +1357,27 @@ ExpectedType ASTNodeImporter::VisitTypedefType(const TypedefType *T) { Expected<TypedefNameDecl *> ToDeclOrErr = import(T->getDecl()); if (!ToDeclOrErr) return ToDeclOrErr.takeError(); + ExpectedType ToUnderlyingTypeOrErr = import(T->desugar()); + if (!ToUnderlyingTypeOrErr) + return ToUnderlyingTypeOrErr.takeError(); - return Importer.getToContext().getTypeDeclType(*ToDeclOrErr); + return Importer.getToContext().getTypedefType(*ToDeclOrErr, + *ToUnderlyingTypeOrErr); } ExpectedType ASTNodeImporter::VisitTypeOfExprType(const TypeOfExprType *T) { ExpectedExpr ToExprOrErr = import(T->getUnderlyingExpr()); if (!ToExprOrErr) return ToExprOrErr.takeError(); - - return Importer.getToContext().getTypeOfExprType(*ToExprOrErr); + return Importer.getToContext().getTypeOfExprType(*ToExprOrErr, T->getKind()); } ExpectedType ASTNodeImporter::VisitTypeOfType(const TypeOfType *T) { - ExpectedType ToUnderlyingTypeOrErr = import(T->getUnderlyingType()); + ExpectedType ToUnderlyingTypeOrErr = import(T->getUnmodifiedType()); if (!ToUnderlyingTypeOrErr) return ToUnderlyingTypeOrErr.takeError(); - - return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr); + return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr, + T->getKind()); } ExpectedType ASTNodeImporter::VisitUsingType(const UsingType *T) { @@ -1432,9 +1430,7 @@ ExpectedType ASTNodeImporter::VisitAutoType(const AutoType *T) { return ToTypeConstraintConcept.takeError(); SmallVector<TemplateArgument, 2> ToTemplateArgs; - ArrayRef<TemplateArgument> FromTemplateArgs = T->getTypeConstraintArguments(); - if (Error Err = ImportTemplateArguments(FromTemplateArgs.data(), - FromTemplateArgs.size(), + if (Error Err = ImportTemplateArguments(T->getTypeConstraintArguments(), ToTemplateArgs)) return std::move(Err); @@ -1520,8 +1516,7 @@ ExpectedType ASTNodeImporter::VisitTemplateTypeParmType( ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmType( const SubstTemplateTypeParmType *T) { - Expected<const TemplateTypeParmType *> ReplacedOrErr = - import(T->getReplacedParameter()); + Expected<Decl *> ReplacedOrErr = import(T->getAssociatedDecl()); if (!ReplacedOrErr) return ReplacedOrErr.takeError(); @@ -1530,13 +1525,13 @@ ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmType( return ToReplacementTypeOrErr.takeError(); return Importer.getToContext().getSubstTemplateTypeParmType( - *ReplacedOrErr, ToReplacementTypeOrErr->getCanonicalType()); + *ToReplacementTypeOrErr, *ReplacedOrErr, T->getIndex(), + T->getPackIndex()); } ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmPackType( const SubstTemplateTypeParmPackType *T) { - Expected<const TemplateTypeParmType *> ReplacedOrErr = - import(T->getReplacedParameter()); + Expected<Decl *> ReplacedOrErr = import(T->getAssociatedDecl()); if (!ReplacedOrErr) return ReplacedOrErr.takeError(); @@ -1545,7 +1540,7 @@ ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmPackType( return ToArgumentPack.takeError(); return Importer.getToContext().getSubstTemplateTypeParmPackType( - *ReplacedOrErr, *ToArgumentPack); + *ReplacedOrErr, T->getIndex(), T->getFinal(), *ToArgumentPack); } ExpectedType ASTNodeImporter::VisitTemplateSpecializationType( @@ -1555,8 +1550,8 @@ ExpectedType ASTNodeImporter::VisitTemplateSpecializationType( return ToTemplateOrErr.takeError(); SmallVector<TemplateArgument, 2> ToTemplateArgs; - if (Error Err = ImportTemplateArguments( - T->getArgs(), T->getNumArgs(), ToTemplateArgs)) + if (Error Err = + ImportTemplateArguments(T->template_arguments(), ToTemplateArgs)) return std::move(Err); QualType ToCanonType; @@ -1613,9 +1608,8 @@ ExpectedType ASTNodeImporter::VisitDependentTemplateSpecializationType( IdentifierInfo *ToName = Importer.Import(T->getIdentifier()); SmallVector<TemplateArgument, 2> ToPack; - ToPack.reserve(T->getNumArgs()); - if (Error Err = ImportTemplateArguments( - T->getArgs(), T->getNumArgs(), ToPack)) + ToPack.reserve(T->template_arguments().size()); + if (Error Err = ImportTemplateArguments(T->template_arguments(), ToPack)) return std::move(Err); return Importer.getToContext().getDependentTemplateSpecializationType( @@ -2186,10 +2180,10 @@ Error ASTNodeImporter::ImportDefinition( } Error ASTNodeImporter::ImportTemplateArguments( - const TemplateArgument *FromArgs, unsigned NumFromArgs, + ArrayRef<TemplateArgument> FromArgs, SmallVectorImpl<TemplateArgument> &ToArgs) { - for (unsigned I = 0; I != NumFromArgs; ++I) { - if (auto ToOrErr = import(FromArgs[I])) + for (const auto &Arg : FromArgs) { + if (auto ToOrErr = import(Arg)) ToArgs.push_back(*ToOrErr); else return ToOrErr.takeError(); @@ -2417,10 +2411,10 @@ ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) { // Create the "to" namespace, if needed. NamespaceDecl *ToNamespace = MergeWithNamespace; if (!ToNamespace) { - if (GetImportedOrCreateDecl( - ToNamespace, D, Importer.getToContext(), DC, D->isInline(), - *BeginLocOrErr, Loc, Name.getAsIdentifierInfo(), - /*PrevDecl=*/nullptr)) + if (GetImportedOrCreateDecl(ToNamespace, D, Importer.getToContext(), DC, + D->isInline(), *BeginLocOrErr, Loc, + Name.getAsIdentifierInfo(), + /*PrevDecl=*/nullptr, D->isNested())) return ToNamespace; ToNamespace->setRBraceLoc(*RBraceLocOrErr); ToNamespace->setLexicalDeclContext(LexicalDC); @@ -3179,10 +3173,10 @@ Error ASTNodeImporter::ImportTemplateInformation( // Import TemplateArgumentListInfo. TemplateArgumentListInfo ToTAInfo; if (Error Err = ImportTemplateArgumentListInfo( - FromInfo->getLAngleLoc(), FromInfo->getRAngleLoc(), - llvm::makeArrayRef( - FromInfo->getTemplateArgs(), FromInfo->getNumTemplateArgs()), - ToTAInfo)) + FromInfo->getLAngleLoc(), FromInfo->getRAngleLoc(), + llvm::ArrayRef(FromInfo->getTemplateArgs(), + FromInfo->getNumTemplateArgs()), + ToTAInfo)) return Err; ToFD->setDependentTemplateSpecialization(Importer.getToContext(), @@ -3234,70 +3228,182 @@ static bool isAncestorDeclContextOf(const DeclContext *DC, const Decl *D) { return false; } -// Returns true if the statement S has a parent declaration that has a -// DeclContext that is inside (or equal to) DC. In a specific use case if DC is -// a FunctionDecl, check if statement S resides in the body of the function. +// Check if there is a declaration that has 'DC' as parent context and is +// referenced from statement 'S' or one of its children. The search is done in +// BFS order through children of 'S'. static bool isAncestorDeclContextOf(const DeclContext *DC, const Stmt *S) { - ParentMapContext &ParentC = DC->getParentASTContext().getParentMapContext(); - DynTypedNodeList Parents = ParentC.getParents(*S); - while (!Parents.empty()) { - if (const Decl *PD = Parents.begin()->get<Decl>()) - return isAncestorDeclContextOf(DC, PD); - Parents = ParentC.getParents(*Parents.begin()); + SmallVector<const Stmt *> ToProcess; + ToProcess.push_back(S); + while (!ToProcess.empty()) { + const Stmt *CurrentS = ToProcess.pop_back_val(); + ToProcess.append(CurrentS->child_begin(), CurrentS->child_end()); + if (const auto *DeclRef = dyn_cast<DeclRefExpr>(CurrentS)) + if (const Decl *D = DeclRef->getDecl()) + if (isAncestorDeclContextOf(DC, D)) + return true; } return false; } -static bool hasTypeDeclaredInsideFunction(QualType T, const FunctionDecl *FD) { - if (T.isNull()) +namespace { +/// Check if a type has any reference to a declaration that is inside the body +/// of a function. +/// The \c CheckType(QualType) function should be used to determine +/// this property. +/// +/// The type visitor visits one type object only (not recursive). +/// To find all referenced declarations we must discover all type objects until +/// the canonical type is reached (walk over typedef and similar objects). This +/// is done by loop over all "sugar" type objects. For every such type we must +/// check all declarations that are referenced from it. For this check the +/// visitor is used. In the visit functions all referenced declarations except +/// the one that follows in the sugar chain (if any) must be checked. For this +/// check the same visitor is re-used (it has no state-dependent data). +/// +/// The visit functions have 3 possible return values: +/// - True, found a declaration inside \c ParentDC. +/// - False, found declarations only outside \c ParentDC and it is not possible +/// to find more declarations (the "sugar" chain does not continue). +/// - Empty optional value, found no declarations or only outside \c ParentDC, +/// but it is possible to find more declarations in the type "sugar" chain. +/// The loop over the "sugar" types can be implemented by using type visit +/// functions only (call \c CheckType with the desugared type). With the current +/// solution no visit function is needed if the type has only a desugared type +/// as data. +class IsTypeDeclaredInsideVisitor + : public TypeVisitor<IsTypeDeclaredInsideVisitor, std::optional<bool>> { +public: + IsTypeDeclaredInsideVisitor(const FunctionDecl *ParentDC) + : ParentDC(ParentDC) {} + + bool CheckType(QualType T) { + // Check the chain of "sugar" types. + // The "sugar" types are typedef or similar types that have the same + // canonical type. + if (std::optional<bool> Res = Visit(T.getTypePtr())) + return *Res; + QualType DsT = + T.getSingleStepDesugaredType(ParentDC->getParentASTContext()); + while (DsT != T) { + if (std::optional<bool> Res = Visit(DsT.getTypePtr())) + return *Res; + T = DsT; + DsT = T.getSingleStepDesugaredType(ParentDC->getParentASTContext()); + } return false; + } + + std::optional<bool> VisitTagType(const TagType *T) { + if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(T->getDecl())) + for (const auto &Arg : Spec->getTemplateArgs().asArray()) + if (checkTemplateArgument(Arg)) + return true; + return isAncestorDeclContextOf(ParentDC, T->getDecl()); + } + + std::optional<bool> VisitPointerType(const PointerType *T) { + return CheckType(T->getPointeeType()); + } + + std::optional<bool> VisitReferenceType(const ReferenceType *T) { + return CheckType(T->getPointeeTypeAsWritten()); + } + + std::optional<bool> VisitTypedefType(const TypedefType *T) { + const TypedefNameDecl *TD = T->getDecl(); + assert(TD); + return isAncestorDeclContextOf(ParentDC, TD); + } + + std::optional<bool> VisitUsingType(const UsingType *T) { + if (T->getFoundDecl() && + isAncestorDeclContextOf(ParentDC, T->getFoundDecl())) + return true; + + return {}; + } - auto CheckTemplateArgument = [FD](const TemplateArgument &Arg) { + std::optional<bool> + VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + for (const auto &Arg : T->template_arguments()) + if (checkTemplateArgument(Arg)) + return true; + // This type is a "sugar" to a record type, it can have a desugared type. + return {}; + } + + std::optional<bool> VisitConstantArrayType(const ConstantArrayType *T) { + if (T->getSizeExpr() && isAncestorDeclContextOf(ParentDC, T->getSizeExpr())) + return true; + + return CheckType(T->getElementType()); + } + + std::optional<bool> VisitVariableArrayType(const VariableArrayType *T) { + llvm_unreachable( + "Variable array should not occur in deduced return type of a function"); + } + + std::optional<bool> VisitIncompleteArrayType(const IncompleteArrayType *T) { + llvm_unreachable("Incomplete array should not occur in deduced return type " + "of a function"); + } + + std::optional<bool> VisitDependentArrayType(const IncompleteArrayType *T) { + llvm_unreachable("Dependent array should not occur in deduced return type " + "of a function"); + } + +private: + const DeclContext *const ParentDC; + + bool checkTemplateArgument(const TemplateArgument &Arg) { switch (Arg.getKind()) { + case TemplateArgument::Null: + return false; + case TemplateArgument::Integral: + return CheckType(Arg.getIntegralType()); case TemplateArgument::Type: - return hasTypeDeclaredInsideFunction(Arg.getAsType(), FD); + return CheckType(Arg.getAsType()); case TemplateArgument::Expression: - return isAncestorDeclContextOf(FD, Arg.getAsExpr()); - default: - // FIXME: Handle other argument kinds. + return isAncestorDeclContextOf(ParentDC, Arg.getAsExpr()); + case TemplateArgument::Declaration: + // FIXME: The declaration in this case is not allowed to be in a function? + return isAncestorDeclContextOf(ParentDC, Arg.getAsDecl()); + case TemplateArgument::NullPtr: + // FIXME: The type is not allowed to be in the function? + return CheckType(Arg.getNullPtrType()); + case TemplateArgument::Pack: + for (const auto &PackArg : Arg.getPackAsArray()) + if (checkTemplateArgument(PackArg)) + return true; + return false; + case TemplateArgument::Template: + // Templates can not be defined locally in functions. + // A template passed as argument can be not in ParentDC. + return false; + case TemplateArgument::TemplateExpansion: + // Templates can not be defined locally in functions. + // A template passed as argument can be not in ParentDC. return false; } + llvm_unreachable("Unknown TemplateArgument::ArgKind enum"); }; - - if (const auto *RecordT = T->getAs<RecordType>()) { - const RecordDecl *RD = RecordT->getDecl(); - assert(RD); - if (isAncestorDeclContextOf(FD, RD)) { - assert(RD->getLexicalDeclContext() == RD->getDeclContext()); - return true; - } - if (const auto *RDTempl = dyn_cast<ClassTemplateSpecializationDecl>(RD)) - if (llvm::count_if(RDTempl->getTemplateArgs().asArray(), - CheckTemplateArgument)) - return true; - // Note: It is possible that T can be get as both a RecordType and a - // TemplateSpecializationType. - } - if (const auto *TST = T->getAs<TemplateSpecializationType>()) - return llvm::count_if(TST->template_arguments(), CheckTemplateArgument); - - return false; -} +}; +} // namespace bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) { QualType FromTy = D->getType(); const auto *FromFPT = FromTy->getAs<FunctionProtoType>(); assert(FromFPT && "Must be called on FunctionProtoType"); - if (const AutoType *AutoT = FromFPT->getReturnType()->getContainedAutoType()) - return hasTypeDeclaredInsideFunction(AutoT->getDeducedType(), D); - if (const auto *TypedefT = FromFPT->getReturnType()->getAs<TypedefType>()) { - const TypedefNameDecl *TD = TypedefT->getDecl(); - assert(TD); - if (isAncestorDeclContextOf(D, TD)) { - assert(TD->getLexicalDeclContext() == TD->getDeclContext()); - return true; - } + + QualType RetT = FromFPT->getReturnType(); + if (isa<AutoType>(RetT.getTypePtr())) { + FunctionDecl *Def = D->getDefinition(); + IsTypeDeclaredInsideVisitor Visitor(Def ? Def : D); + return Visitor.CheckType(RetT); } + return false; } @@ -3475,6 +3581,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { auto TInfo = importChecked(Err, FromTSI); auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart()); auto ToEndLoc = importChecked(Err, D->getEndLoc()); + auto ToDefaultLoc = importChecked(Err, D->getDefaultLoc()); auto ToQualifierLoc = importChecked(Err, D->getQualifierLoc()); auto TrailingRequiresClause = importChecked(Err, D->getTrailingRequiresClause()); @@ -3483,7 +3590,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // Import the function parameters. SmallVector<ParmVarDecl *, 8> Parameters; - for (auto P : D->parameters()) { + for (auto *P : D->parameters()) { if (Expected<ParmVarDecl *> ToPOrErr = import(P)) Parameters.push_back(*ToPOrErr); else @@ -3592,7 +3699,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction->setDefaulted(D->isDefaulted()); ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted()); ToFunction->setDeletedAsWritten(D->isDeletedAsWritten()); + ToFunction->setFriendConstraintRefersToEnclosingTemplate( + D->FriendConstraintRefersToEnclosingTemplate()); ToFunction->setRangeEnd(ToEndLoc); + ToFunction->setDefaultLoc(ToDefaultLoc); // Set the parameters. for (auto *Param : Parameters) { @@ -3637,6 +3747,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { } } + // If it is a template, import all related things. + if (Error Err = ImportTemplateInformation(D, ToFunction)) + return std::move(Err); + if (D->doesThisDeclarationHaveABody()) { Error Err = ImportFunctionDeclBody(D, ToFunction); @@ -3658,10 +3772,6 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // FIXME: Other bits to merge? - // If it is a template, import all related things. - if (Error Err = ImportTemplateInformation(D, ToFunction)) - return std::move(Err); - addDeclToContexts(D, ToFunction); if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D)) @@ -3870,7 +3980,7 @@ static FriendCountAndPosition getFriendCountAndPosition( const FriendDecl *FD, llvm::function_ref<T(const FriendDecl *)> GetCanTypeOrDecl) { unsigned int FriendCount = 0; - llvm::Optional<unsigned int> FriendPosition; + std::optional<unsigned int> FriendPosition; const auto *RD = cast<CXXRecordDecl>(FD->getLexicalDeclContext()); T TypeOrDecl = GetCanTypeOrDecl(FD); @@ -4754,13 +4864,14 @@ ExpectedDecl ASTNodeImporter::VisitUsingEnumDecl(UsingEnumDecl *D) { Error Err = Error::success(); auto ToUsingLoc = importChecked(Err, D->getUsingLoc()); auto ToEnumLoc = importChecked(Err, D->getEnumLoc()); - auto ToEnumDecl = importChecked(Err, D->getEnumDecl()); + auto ToNameLoc = importChecked(Err, D->getLocation()); + auto *ToEnumType = importChecked(Err, D->getEnumType()); if (Err) return std::move(Err); UsingEnumDecl *ToUsingEnum; if (GetImportedOrCreateDecl(ToUsingEnum, D, Importer.getToContext(), DC, - ToUsingLoc, ToEnumLoc, Loc, ToEnumDecl)) + ToUsingLoc, ToEnumLoc, ToNameLoc, ToEnumType)) return ToUsingEnum; ToUsingEnum->setLexicalDeclContext(LexicalDC); @@ -5722,8 +5833,8 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( // Import template arguments. SmallVector<TemplateArgument, 2> TemplateArgs; - if (Error Err = ImportTemplateArguments( - D->getTemplateArgs().data(), D->getTemplateArgs().size(), TemplateArgs)) + if (Error Err = + ImportTemplateArguments(D->getTemplateArgs().asArray(), TemplateArgs)) return std::move(Err); // Try to find an existing specialization with these template arguments and // template parameter list. @@ -5804,10 +5915,10 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( CanonInjType = CanonInjType.getCanonicalType(); if (GetImportedOrCreateDecl<ClassTemplatePartialSpecializationDecl>( - D2, D, Importer.getToContext(), D->getTagKind(), DC, - *BeginLocOrErr, *IdLocOrErr, ToTPList, ClassTemplate, - llvm::makeArrayRef(TemplateArgs.data(), TemplateArgs.size()), - ToTAInfo, CanonInjType, + D2, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr, + *IdLocOrErr, ToTPList, ClassTemplate, + llvm::ArrayRef(TemplateArgs.data(), TemplateArgs.size()), ToTAInfo, + CanonInjType, cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl))) return D2; @@ -5881,6 +5992,30 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( D2->setTemplateSpecializationKind(D->getTemplateSpecializationKind()); + if (auto P = D->getInstantiatedFrom()) { + if (auto *CTD = P.dyn_cast<ClassTemplateDecl *>()) { + if (auto CTDorErr = import(CTD)) + D2->setInstantiationOf(*CTDorErr); + } else { + auto *CTPSD = cast<ClassTemplatePartialSpecializationDecl *>(P); + auto CTPSDOrErr = import(CTPSD); + if (!CTPSDOrErr) + return CTPSDOrErr.takeError(); + const TemplateArgumentList &DArgs = D->getTemplateInstantiationArgs(); + SmallVector<TemplateArgument, 2> D2ArgsVec(DArgs.size()); + for (unsigned I = 0; I < DArgs.size(); ++I) { + const TemplateArgument &DArg = DArgs[I]; + if (auto ArgOrErr = import(DArg)) + D2ArgsVec[I] = *ArgOrErr; + else + return ArgOrErr.takeError(); + } + D2->setInstantiationOf( + *CTPSDOrErr, + TemplateArgumentList::CreateCopy(Importer.getToContext(), D2ArgsVec)); + } + } + if (D->isCompleteDefinition()) if (Error Err = ImportDefinition(D, D2)) return std::move(Err); @@ -6020,8 +6155,8 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl( // Import template arguments. SmallVector<TemplateArgument, 2> TemplateArgs; - if (Error Err = ImportTemplateArguments( - D->getTemplateArgs().data(), D->getTemplateArgs().size(), TemplateArgs)) + if (Error Err = + ImportTemplateArguments(D->getTemplateArgs().asArray(), TemplateArgs)) return std::move(Err); // Try to find an existing specialization with these template arguments. @@ -6044,15 +6179,6 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl( } } } else { - // Import the type. - QualType T; - if (Error Err = importInto(T, D->getType())) - return std::move(Err); - - auto TInfoOrErr = import(D->getTypeSourceInfo()); - if (!TInfoOrErr) - return TInfoOrErr.takeError(); - TemplateArgumentListInfo ToTAInfo; if (const ASTTemplateArgumentListInfo *Args = D->getTemplateArgsInfo()) { if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo)) @@ -6077,7 +6203,7 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl( PartVarSpecDecl *ToPartial; if (GetImportedOrCreateDecl(ToPartial, D, Importer.getToContext(), DC, *BeginLocOrErr, *IdLocOrErr, *ToTPListOrErr, - VarTemplate, T, *TInfoOrErr, + VarTemplate, QualType(), nullptr, D->getStorageClass(), TemplateArgs, ArgInfos)) return ToPartial; @@ -6098,11 +6224,21 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl( } else { // Full specialization if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC, *BeginLocOrErr, *IdLocOrErr, VarTemplate, - T, *TInfoOrErr, - D->getStorageClass(), TemplateArgs)) + QualType(), nullptr, D->getStorageClass(), + TemplateArgs)) return D2; } + QualType T; + if (Error Err = importInto(T, D->getType())) + return std::move(Err); + D2->setType(T); + + auto TInfoOrErr = import(D->getTypeSourceInfo()); + if (!TInfoOrErr) + return TInfoOrErr.takeError(); + D2->setTypeSourceInfo(*TInfoOrErr); + if (D->getPointOfInstantiation().isValid()) { if (ExpectedSLoc POIOrErr = import(D->getPointOfInstantiation())) D2->setPointOfInstantiation(*POIOrErr); @@ -6875,14 +7011,14 @@ ASTNodeImporter::VisitGenericSelectionExpr(GenericSelectionExpr *E) { const ASTContext &ToCtx = Importer.getToContext(); if (E->isResultDependent()) { return GenericSelectionExpr::Create( - ToCtx, ToGenericLoc, ToControllingExpr, - llvm::makeArrayRef(ToAssocTypes), llvm::makeArrayRef(ToAssocExprs), - ToDefaultLoc, ToRParenLoc, E->containsUnexpandedParameterPack()); + ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes), + llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc, + E->containsUnexpandedParameterPack()); } return GenericSelectionExpr::Create( - ToCtx, ToGenericLoc, ToControllingExpr, llvm::makeArrayRef(ToAssocTypes), - llvm::makeArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc, + ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes), + llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc, E->containsUnexpandedParameterPack(), E->getResultIndex()); } @@ -7224,7 +7360,7 @@ ExpectedStmt ASTNodeImporter::VisitBinaryOperator(BinaryOperator *E) { return BinaryOperator::Create( Importer.getToContext(), ToLHS, ToRHS, E->getOpcode(), ToType, E->getValueKind(), E->getObjectKind(), ToOperatorLoc, - E->getFPFeatures(Importer.getFromContext().getLangOpts())); + E->getFPFeatures()); } ExpectedStmt ASTNodeImporter::VisitConditionalOperator(ConditionalOperator *E) { @@ -7335,7 +7471,7 @@ ASTNodeImporter::VisitCompoundAssignOperator(CompoundAssignOperator *E) { return CompoundAssignOperator::Create( Importer.getToContext(), ToLHS, ToRHS, E->getOpcode(), ToType, E->getValueKind(), E->getObjectKind(), ToOperatorLoc, - E->getFPFeatures(Importer.getFromContext().getLangOpts()), + E->getFPFeatures(), ToComputationLHSType, ToComputationResultType); } @@ -7544,15 +7680,23 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { // see VisitParmVarDecl). ParmVarDecl *ToParam = *ToParamOrErr; if (!ToParam->getDefaultArg()) { - Optional<ParmVarDecl *> FromParam = Importer.getImportedFromDecl(ToParam); + std::optional<ParmVarDecl *> FromParam = + Importer.getImportedFromDecl(ToParam); assert(FromParam && "ParmVarDecl was not imported?"); if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam)) return std::move(Err); } - + Expr *RewrittenInit = nullptr; + if (E->hasRewrittenInit()) { + ExpectedExpr ExprOrErr = import(E->getRewrittenExpr()); + if (!ExprOrErr) + return ExprOrErr.takeError(); + RewrittenInit = ExprOrErr.get(); + } return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr, - *ToParamOrErr, *UsedContextOrErr); + *ToParamOrErr, RewrittenInit, + *UsedContextOrErr); } ExpectedStmt @@ -7670,16 +7814,14 @@ ExpectedStmt ASTNodeImporter::VisitSizeOfPackExpr(SizeOfPackExpr *E) { if (Err) return std::move(Err); - Optional<unsigned> Length; + std::optional<unsigned> Length; if (!E->isValueDependent()) Length = E->getPackLength(); SmallVector<TemplateArgument, 8> ToPartialArguments; if (E->isPartiallySubstituted()) { - if (Error Err = ImportTemplateArguments( - E->getPartialArguments().data(), - E->getPartialArguments().size(), - ToPartialArguments)) + if (Error Err = ImportTemplateArguments(E->getPartialArguments(), + ToPartialArguments)) return std::move(Err); } @@ -7806,8 +7948,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { if (!ToLocationOrErr) return ToLocationOrErr.takeError(); - return new (Importer.getToContext()) CXXBoolLiteralExpr( - E->getValue(), *ToTypeOrErr, *ToLocationOrErr); + return CXXBoolLiteralExpr::Create(Importer.getToContext(), E->getValue(), + *ToTypeOrErr, *ToLocationOrErr); } ExpectedStmt ASTNodeImporter::VisitMemberExpr(MemberExpr *E) { @@ -7969,7 +8111,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXUnresolvedConstructExpr( return CXXUnresolvedConstructExpr::Create( Importer.getToContext(), ToType, ToTypeSourceInfo, ToLParenLoc, - llvm::makeArrayRef(ToArgs), ToRParenLoc); + llvm::ArrayRef(ToArgs), ToRParenLoc); } ExpectedStmt @@ -8246,8 +8388,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { ToField->setInClassInitializer(*ToInClassInitializerOrErr); } + Expr *RewrittenInit = nullptr; + if (E->hasRewrittenInit()) { + ExpectedExpr ExprOrErr = import(E->getRewrittenExpr()); + if (!ExprOrErr) + return ExprOrErr.takeError(); + RewrittenInit = ExprOrErr.get(); + } + return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr, - ToField, *UsedContextOrErr); + ToField, *UsedContextOrErr, RewrittenInit); } ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) { @@ -8295,14 +8445,14 @@ ExpectedStmt ASTNodeImporter::VisitSubstNonTypeTemplateParmExpr( Error Err = Error::success(); auto ToType = importChecked(Err, E->getType()); auto ToExprLoc = importChecked(Err, E->getExprLoc()); - auto ToParameter = importChecked(Err, E->getParameter()); + auto ToAssociatedDecl = importChecked(Err, E->getAssociatedDecl()); auto ToReplacement = importChecked(Err, E->getReplacement()); if (Err) return std::move(Err); return new (Importer.getToContext()) SubstNonTypeTemplateParmExpr( - ToType, E->getValueKind(), ToExprLoc, ToParameter, - E->isReferenceParameter(), ToReplacement); + ToType, E->getValueKind(), ToExprLoc, ToReplacement, ToAssociatedDecl, + E->getIndex(), E->getPackIndex(), E->isReferenceParameter()); } ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) { @@ -8403,13 +8553,13 @@ ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, ASTImporter::~ASTImporter() = default; -Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) { +std::optional<unsigned> ASTImporter::getFieldIndex(Decl *F) { assert(F && (isa<FieldDecl>(*F) || isa<IndirectFieldDecl>(*F)) && "Try to get field index for non-field."); auto *Owner = dyn_cast<RecordDecl>(F->getDeclContext()); if (!Owner) - return None; + return std::nullopt; unsigned Index = 0; for (const auto *D : Owner->decls()) { @@ -8422,7 +8572,7 @@ Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) { llvm_unreachable("Field was not found in its parent context."); - return None; + return std::nullopt; } ASTImporter::FoundDeclsTy @@ -8533,6 +8683,7 @@ Expected<TypeSourceInfo *> ASTImporter::Import(TypeSourceInfo *FromTSI) { return ToContext.getTrivialTypeSourceInfo(*TOrErr, *BeginLocOrErr); } +namespace { // To use this object, it should be created before the new attribute is created, // and destructed after it is created. The construction already performs the // import of the data. @@ -8663,6 +8814,7 @@ public: return ToAttr; } }; +} // namespace Expected<Attr *> ASTImporter::Import(const Attr *FromAttr) { AttrImporter AI(*this); @@ -9279,33 +9431,35 @@ Expected<TemplateName> ASTImporter::Import(TemplateName From) { case TemplateName::SubstTemplateTemplateParm: { SubstTemplateTemplateParmStorage *Subst = From.getAsSubstTemplateTemplateParm(); - ExpectedDecl ParamOrErr = Import(Subst->getParameter()); - if (!ParamOrErr) - return ParamOrErr.takeError(); - auto ReplacementOrErr = Import(Subst->getReplacement()); if (!ReplacementOrErr) return ReplacementOrErr.takeError(); + auto AssociatedDeclOrErr = Import(Subst->getAssociatedDecl()); + if (!AssociatedDeclOrErr) + return AssociatedDeclOrErr.takeError(); + return ToContext.getSubstTemplateTemplateParm( - cast<TemplateTemplateParmDecl>(*ParamOrErr), *ReplacementOrErr); + *ReplacementOrErr, *AssociatedDeclOrErr, Subst->getIndex(), + Subst->getPackIndex()); } case TemplateName::SubstTemplateTemplateParmPack: { - SubstTemplateTemplateParmPackStorage *SubstPack - = From.getAsSubstTemplateTemplateParmPack(); - ExpectedDecl ParamOrErr = Import(SubstPack->getParameterPack()); - if (!ParamOrErr) - return ParamOrErr.takeError(); - + SubstTemplateTemplateParmPackStorage *SubstPack = + From.getAsSubstTemplateTemplateParmPack(); ASTNodeImporter Importer(*this); auto ArgPackOrErr = Importer.ImportTemplateArgument(SubstPack->getArgumentPack()); if (!ArgPackOrErr) return ArgPackOrErr.takeError(); + auto AssociatedDeclOrErr = Import(SubstPack->getAssociatedDecl()); + if (!AssociatedDeclOrErr) + return AssociatedDeclOrErr.takeError(); + return ToContext.getSubstTemplateTemplateParmPack( - cast<TemplateTemplateParmDecl>(*ParamOrErr), *ArgPackOrErr); + *ArgPackOrErr, *AssociatedDeclOrErr, SubstPack->getIndex(), + SubstPack->getFinal()); } case TemplateName::UsingTemplate: { auto UsingOrError = Import(From.getAsUsingShadowDecl()); @@ -9410,7 +9564,7 @@ Expected<FileID> ASTImporter::Import(FileID FromID, bool IsBuiltin) { if (ToID.isInvalid() || IsBuiltin) { // FIXME: We want to re-use the existing MemoryBuffer! - llvm::Optional<llvm::MemoryBufferRef> FromBuf = + std::optional<llvm::MemoryBufferRef> FromBuf = Cache->getBufferOrNone(FromContext.getDiagnostics(), FromSM.getFileManager(), SourceLocation{}); if (!FromBuf) @@ -9879,13 +10033,13 @@ Decl *ASTImporter::MapImported(Decl *From, Decl *To) { return To; } -llvm::Optional<ASTImportError> +std::optional<ASTImportError> ASTImporter::getImportDeclErrorIfAny(Decl *FromD) const { auto Pos = ImportDeclErrors.find(FromD); if (Pos != ImportDeclErrors.end()) return Pos->second; else - return Optional<ASTImportError>(); + return std::nullopt; } void ASTImporter::setImportDeclError(Decl *From, ASTImportError Error) { diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d80fc3ce7292..ba7dfc35edf2 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -84,13 +84,12 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include <cassert> +#include <optional> #include <utility> using namespace clang; @@ -103,6 +102,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, const TemplateArgument &Arg1, const TemplateArgument &Arg2); static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateArgumentLoc &Arg1, + const TemplateArgumentLoc &Arg2); +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, NestedNameSpecifier *NNS1, NestedNameSpecifier *NNS2); static bool IsStructurallyEquivalent(const IdentifierInfo *Name1, @@ -238,8 +240,8 @@ class StmtComparer { const GenericSelectionExpr *E2) { for (auto Pair : zip_longest(E1->getAssocTypeSourceInfos(), E2->getAssocTypeSourceInfos())) { - Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair); - Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair); + std::optional<TypeSourceInfo *> Child1 = std::get<0>(Pair); + std::optional<TypeSourceInfo *> Child2 = std::get<1>(Pair); // Skip this case if there are a different number of associated types. if (!Child1 || !Child2) return false; @@ -289,8 +291,14 @@ class StmtComparer { bool IsStmtEquivalent(const SubstNonTypeTemplateParmExpr *E1, const SubstNonTypeTemplateParmExpr *E2) { - return IsStructurallyEquivalent(Context, E1->getParameter(), - E2->getParameter()); + if (!IsStructurallyEquivalent(Context, E1->getAssociatedDecl(), + E2->getAssociatedDecl())) + return false; + if (E1->getIndex() != E2->getIndex()) + return false; + if (E1->getPackIndex() != E2->getPackIndex()) + return false; + return true; } bool IsStmtEquivalent(const SubstNonTypeTemplateParmPackExpr *E1, @@ -304,8 +312,8 @@ class StmtComparer { return false; for (auto Pair : zip_longest(E1->getArgs(), E2->getArgs())) { - Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair); - Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair); + std::optional<TypeSourceInfo *> Child1 = std::get<0>(Pair); + std::optional<TypeSourceInfo *> Child2 = std::get<1>(Pair); // Different number of args. if (!Child1 || !Child2) return false; @@ -334,6 +342,30 @@ class StmtComparer { return true; } + bool IsStmtEquivalent(const OverloadExpr *E1, const OverloadExpr *E2) { + if (!IsStructurallyEquivalent(Context, E1->getName(), E2->getName())) + return false; + + if (static_cast<bool>(E1->getQualifier()) != + static_cast<bool>(E2->getQualifier())) + return false; + if (E1->getQualifier() && + !IsStructurallyEquivalent(Context, E1->getQualifier(), + E2->getQualifier())) + return false; + + if (E1->getNumTemplateArgs() != E2->getNumTemplateArgs()) + return false; + const TemplateArgumentLoc *Args1 = E1->getTemplateArgs(); + const TemplateArgumentLoc *Args2 = E2->getTemplateArgs(); + for (unsigned int ArgI = 0, ArgN = E1->getNumTemplateArgs(); ArgI < ArgN; + ++ArgI) + if (!IsStructurallyEquivalent(Context, Args1[ArgI], Args2[ArgI])) + return false; + + return true; + } + /// End point of the traversal chain. bool TraverseStmt(const Stmt *S1, const Stmt *S2) { return true; } @@ -394,8 +426,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, // Iterate over the children of both statements and also compare them. for (auto Pair : zip_longest(S1->children(), S2->children())) { - Optional<const Stmt *> Child1 = std::get<0>(Pair); - Optional<const Stmt *> Child2 = std::get<1>(Pair); + std::optional<const Stmt *> Child1 = std::get<0>(Pair); + std::optional<const Stmt *> Child2 = std::get<1>(Pair); // One of the statements has a different amount of children than the other, // so the statements can't be equivalent. if (!Child1 || !Child2) @@ -510,8 +542,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, *P2 = N2.getAsSubstTemplateTemplateParmPack(); return IsStructurallyEquivalent(Context, P1->getArgumentPack(), P2->getArgumentPack()) && - IsStructurallyEquivalent(Context, P1->getParameterPack(), - P2->getParameterPack()); + IsStructurallyEquivalent(Context, P1->getAssociatedDecl(), + P2->getAssociatedDecl()) && + P1->getIndex() == P2->getIndex(); } case TemplateName::Template: @@ -526,6 +559,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return true; } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + ArrayRef<TemplateArgument> Args1, + ArrayRef<TemplateArgument> Args2); + /// Determine whether two template arguments are equivalent. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, const TemplateArgument &Arg1, @@ -568,18 +605,32 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Arg2.getAsExpr()); case TemplateArgument::Pack: - if (Arg1.pack_size() != Arg2.pack_size()) - return false; + return IsStructurallyEquivalent(Context, Arg1.pack_elements(), + Arg2.pack_elements()); + } - for (unsigned I = 0, N = Arg1.pack_size(); I != N; ++I) - if (!IsStructurallyEquivalent(Context, Arg1.pack_begin()[I], - Arg2.pack_begin()[I])) - return false; + llvm_unreachable("Invalid template argument kind"); +} - return true; +/// Determine structural equivalence of two template argument lists. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + ArrayRef<TemplateArgument> Args1, + ArrayRef<TemplateArgument> Args2) { + if (Args1.size() != Args2.size()) + return false; + for (unsigned I = 0, N = Args1.size(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Args1[I], Args2[I])) + return false; } + return true; +} - llvm_unreachable("Invalid template argument kind"); +/// Determine whether two template argument locations are equivalent. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateArgumentLoc &Arg1, + const TemplateArgumentLoc &Arg2) { + return IsStructurallyEquivalent(Context, Arg1.getArgument(), + Arg2.getArgument()); } /// Determine structural equivalence for the common part of array @@ -900,7 +951,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; // Fall through to check the bits common with FunctionNoProtoType. - LLVM_FALLTHROUGH; + [[fallthrough]]; } case Type::FunctionNoProto: { @@ -957,11 +1008,17 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (!IsStructurallyEquivalent(Context, cast<UsingType>(T1)->getFoundDecl(), cast<UsingType>(T2)->getFoundDecl())) return false; + if (!IsStructurallyEquivalent(Context, + cast<UsingType>(T1)->getUnderlyingType(), + cast<UsingType>(T2)->getUnderlyingType())) + return false; break; case Type::Typedef: if (!IsStructurallyEquivalent(Context, cast<TypedefType>(T1)->getDecl(), - cast<TypedefType>(T2)->getDecl())) + cast<TypedefType>(T2)->getDecl()) || + !IsStructurallyEquivalent(Context, cast<TypedefType>(T1)->desugar(), + cast<TypedefType>(T2)->desugar())) return false; break; @@ -974,8 +1031,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case Type::TypeOf: if (!IsStructurallyEquivalent(Context, - cast<TypeOfType>(T1)->getUnderlyingType(), - cast<TypeOfType>(T2)->getUnderlyingType())) + cast<TypeOfType>(T1)->getUnmodifiedType(), + cast<TypeOfType>(T2)->getUnmodifiedType())) return false; break; @@ -1005,16 +1062,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (Auto1->getTypeConstraintConcept() != Auto2->getTypeConstraintConcept()) return false; - ArrayRef<TemplateArgument> Auto1Args = - Auto1->getTypeConstraintArguments(); - ArrayRef<TemplateArgument> Auto2Args = - Auto2->getTypeConstraintArguments(); - if (Auto1Args.size() != Auto2Args.size()) + if (!IsStructurallyEquivalent(Context, + Auto1->getTypeConstraintArguments(), + Auto2->getTypeConstraintArguments())) return false; - for (unsigned I = 0, N = Auto1Args.size(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, Auto1Args[I], Auto2Args[I])) - return false; - } } break; } @@ -1055,22 +1106,26 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case Type::SubstTemplateTypeParm: { const auto *Subst1 = cast<SubstTemplateTypeParmType>(T1); const auto *Subst2 = cast<SubstTemplateTypeParmType>(T2); - if (!IsStructurallyEquivalent(Context, - QualType(Subst1->getReplacedParameter(), 0), - QualType(Subst2->getReplacedParameter(), 0))) - return false; if (!IsStructurallyEquivalent(Context, Subst1->getReplacementType(), Subst2->getReplacementType())) return false; + if (!IsStructurallyEquivalent(Context, Subst1->getAssociatedDecl(), + Subst2->getAssociatedDecl())) + return false; + if (Subst1->getIndex() != Subst2->getIndex()) + return false; + if (Subst1->getPackIndex() != Subst2->getPackIndex()) + return false; break; } case Type::SubstTemplateTypeParmPack: { const auto *Subst1 = cast<SubstTemplateTypeParmPackType>(T1); const auto *Subst2 = cast<SubstTemplateTypeParmPackType>(T2); - if (!IsStructurallyEquivalent(Context, - QualType(Subst1->getReplacedParameter(), 0), - QualType(Subst2->getReplacedParameter(), 0))) + if (!IsStructurallyEquivalent(Context, Subst1->getAssociatedDecl(), + Subst2->getAssociatedDecl())) + return false; + if (Subst1->getIndex() != Subst2->getIndex()) return false; if (!IsStructurallyEquivalent(Context, Subst1->getArgumentPack(), Subst2->getArgumentPack())) @@ -1084,13 +1139,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (!IsStructurallyEquivalent(Context, Spec1->getTemplateName(), Spec2->getTemplateName())) return false; - if (Spec1->getNumArgs() != Spec2->getNumArgs()) + if (!IsStructurallyEquivalent(Context, Spec1->template_arguments(), + Spec2->template_arguments())) return false; - for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, Spec1->getArg(I), - Spec2->getArg(I))) - return false; - } break; } @@ -1141,13 +1192,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (!IsStructurallyEquivalent(Spec1->getIdentifier(), Spec2->getIdentifier())) return false; - if (Spec1->getNumArgs() != Spec2->getNumArgs()) + if (!IsStructurallyEquivalent(Context, Spec1->template_arguments(), + Spec2->template_arguments())) return false; - for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, Spec1->getArg(I), - Spec2->getArg(I))) - return false; - } break; } @@ -1434,9 +1481,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, if (!D1->getDeclName() && !D2->getDeclName()) { // If both anonymous structs/unions are in a record context, make sure // they occur in the same location in the context records. - if (Optional<unsigned> Index1 = + if (std::optional<unsigned> Index1 = StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(D1)) { - if (Optional<unsigned> Index2 = + if (std::optional<unsigned> Index2 = StructuralEquivalenceContext::findUntaggedStructOrUnionIndex( D2)) { if (*Index1 != *Index2) @@ -2108,14 +2155,14 @@ DiagnosticBuilder StructuralEquivalenceContext::Diag2(SourceLocation Loc, return ToCtx.getDiagnostics().Report(Loc, DiagID); } -Optional<unsigned> +std::optional<unsigned> StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(RecordDecl *Anon) { ASTContext &Context = Anon->getASTContext(); QualType AnonTy = Context.getRecordType(Anon); const auto *Owner = dyn_cast<RecordDecl>(Anon->getDeclContext()); if (!Owner) - return None; + return std::nullopt; unsigned Index = 0; for (const auto *D : Owner->noload_decls()) { diff --git a/clang/lib/AST/AttrDocTable.cpp b/clang/lib/AST/AttrDocTable.cpp index 3bfedac8b8f1..df7e3d63a6c3 100644 --- a/clang/lib/AST/AttrDocTable.cpp +++ b/clang/lib/AST/AttrDocTable.cpp @@ -21,7 +21,7 @@ static const llvm::StringRef AttrDoc[] = { }; llvm::StringRef clang::Attr::getDocumentation(clang::attr::Kind K) { - if(K < llvm::array_lengthof(AttrDoc)) + if (K < std::size(AttrDoc)) return AttrDoc[K]; return ""; } diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index deb28bee5ed8..0adcca7731d9 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -14,6 +14,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include <optional> using namespace clang; void LoopHintAttr::printPrettyPragma(raw_ostream &OS, @@ -137,7 +138,7 @@ void OMPDeclareTargetDeclAttr::printPrettyPragma( // Use fake syntax because it is for testing and debugging purpose only. if (getDevType() != DT_Any) OS << " device_type(" << ConvertDevTypeTyToStr(getDevType()) << ")"; - if (getMapType() != MT_To) + if (getMapType() != MT_To && getMapType() != MT_Enter) OS << ' ' << ConvertMapTypeTyToStr(getMapType()); if (Expr *E = getIndirectExpr()) { OS << " indirect("; @@ -148,10 +149,10 @@ void OMPDeclareTargetDeclAttr::printPrettyPragma( } } -llvm::Optional<OMPDeclareTargetDeclAttr *> +std::optional<OMPDeclareTargetDeclAttr *> OMPDeclareTargetDeclAttr::getActiveAttr(const ValueDecl *VD) { if (!VD->hasAttrs()) - return llvm::None; + return std::nullopt; unsigned Level = 0; OMPDeclareTargetDeclAttr *FoundAttr = nullptr; for (auto *Attr : VD->specific_attrs<OMPDeclareTargetDeclAttr>()) { @@ -162,31 +163,31 @@ OMPDeclareTargetDeclAttr::getActiveAttr(const ValueDecl *VD) { } if (FoundAttr) return FoundAttr; - return llvm::None; + return std::nullopt; } -llvm::Optional<OMPDeclareTargetDeclAttr::MapTypeTy> +std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(const ValueDecl *VD) { - llvm::Optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); + std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); if (ActiveAttr) - return ActiveAttr.value()->getMapType(); - return llvm::None; + return (*ActiveAttr)->getMapType(); + return std::nullopt; } -llvm::Optional<OMPDeclareTargetDeclAttr::DevTypeTy> +std::optional<OMPDeclareTargetDeclAttr::DevTypeTy> OMPDeclareTargetDeclAttr::getDeviceType(const ValueDecl *VD) { - llvm::Optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); + std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); if (ActiveAttr) - return ActiveAttr.value()->getDevType(); - return llvm::None; + return (*ActiveAttr)->getDevType(); + return std::nullopt; } -llvm::Optional<SourceLocation> +std::optional<SourceLocation> OMPDeclareTargetDeclAttr::getLocation(const ValueDecl *VD) { - llvm::Optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); + std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = getActiveAttr(VD); if (ActiveAttr) - return ActiveAttr.value()->getRange().getBegin(); - return llvm::None; + return (*ActiveAttr)->getRange().getBegin(); + return std::nullopt; } namespace clang { @@ -222,18 +223,18 @@ void OMPDeclareVariantAttr::printPrettyPragma( OS << ")"; } - auto PrintInteropTypes = [&OS](InteropType *Begin, InteropType *End) { - for (InteropType *I = Begin; I != End; ++I) { + auto PrintInteropInfo = [&OS](OMPInteropInfo *Begin, OMPInteropInfo *End) { + for (OMPInteropInfo *I = Begin; I != End; ++I) { if (I != Begin) OS << ", "; OS << "interop("; - OS << ConvertInteropTypeToStr(*I); + OS << getInteropTypeString(I); OS << ")"; } }; if (appendArgs_size()) { OS << " append_args("; - PrintInteropTypes(appendArgs_begin(), appendArgs_end()); + PrintInteropInfo(appendArgs_begin(), appendArgs_end()); OS << ")"; } } diff --git a/clang/lib/AST/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp index 96a6f344be7c..1abbe8139ae9 100644 --- a/clang/lib/AST/CXXInheritance.cpp +++ b/clang/lib/AST/CXXInheritance.cpp @@ -22,7 +22,6 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Casting.h" diff --git a/clang/lib/AST/Comment.cpp b/clang/lib/AST/Comment.cpp index 43820fc566e4..2bb6bb5cbcb6 100644 --- a/clang/lib/AST/Comment.cpp +++ b/clang/lib/AST/Comment.cpp @@ -29,7 +29,7 @@ namespace comments { #undef ABSTRACT_COMMENT // DeclInfo is also allocated with a BumpPtrAllocator. -static_assert(std::is_trivially_destructible<DeclInfo>::value, +static_assert(std::is_trivially_destructible_v<DeclInfo>, "DeclInfo should be trivially destructible!"); const char *Comment::getCommentKindName() const { @@ -206,7 +206,7 @@ void DeclInfo::fill() { IsInstanceMethod = false; IsClassMethod = false; IsVariadic = false; - ParamVars = None; + ParamVars = std::nullopt; TemplateParameters = nullptr; if (!CommentDecl) { @@ -301,7 +301,7 @@ void DeclInfo::fill() { TemplateKind = TemplateSpecialization; TemplateParameters = VTD->getTemplateParameters(); } - LLVM_FALLTHROUGH; + [[fallthrough]]; case Decl::Field: case Decl::EnumConstant: case Decl::ObjCIvar: diff --git a/clang/lib/AST/CommentCommandTraits.cpp b/clang/lib/AST/CommentCommandTraits.cpp index bdc0dd47fb7d..a37a0e18432c 100644 --- a/clang/lib/AST/CommentCommandTraits.cpp +++ b/clang/lib/AST/CommentCommandTraits.cpp @@ -16,8 +16,8 @@ namespace comments { #include "clang/AST/CommentCommandInfo.inc" CommandTraits::CommandTraits(llvm::BumpPtrAllocator &Allocator, - const CommentOptions &CommentOptions) : - NextID(llvm::array_lengthof(Commands)), Allocator(Allocator) { + const CommentOptions &CommentOptions) + : NextID(std::size(Commands)), Allocator(Allocator) { registerCommentOptions(CommentOptions); } @@ -115,7 +115,7 @@ const CommandInfo *CommandTraits::registerBlockCommand(StringRef CommandName) { const CommandInfo *CommandTraits::getBuiltinCommandInfo( unsigned CommandID) { - if (CommandID < llvm::array_lengthof(Commands)) + if (CommandID < std::size(Commands)) return &Commands[CommandID]; return nullptr; } @@ -131,7 +131,7 @@ const CommandInfo *CommandTraits::getRegisteredCommandInfo( const CommandInfo *CommandTraits::getRegisteredCommandInfo( unsigned CommandID) const { - return RegisteredCommands[CommandID - llvm::array_lengthof(Commands)]; + return RegisteredCommands[CommandID - std::size(Commands)]; } } // end namespace comments diff --git a/clang/lib/AST/CommentLexer.cpp b/clang/lib/AST/CommentLexer.cpp index 61ce8979f13f..f0250fc9fd55 100644 --- a/clang/lib/AST/CommentLexer.cpp +++ b/clang/lib/AST/CommentLexer.cpp @@ -701,7 +701,7 @@ void Lexer::lexHTMLStartTag(Token &T) { C = *BufferPtr; if (!isHTMLIdentifierStartingCharacter(C) && - C != '=' && C != '\"' && C != '\'' && C != '>') { + C != '=' && C != '\"' && C != '\'' && C != '>' && C != '/') { State = LS_Normal; return; } diff --git a/clang/lib/AST/CommentParser.cpp b/clang/lib/AST/CommentParser.cpp index 7bac1fb99b88..8adfd85d0160 100644 --- a/clang/lib/AST/CommentParser.cpp +++ b/clang/lib/AST/CommentParser.cpp @@ -245,7 +245,7 @@ public: Pos.CurToken++; } - P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end())); + P.putBack(llvm::ArrayRef(Toks.begin() + Pos.CurToken, Toks.end())); Pos.CurToken = Toks.size(); if (HavePartialTok) @@ -301,7 +301,7 @@ Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) { ParsedArgs++; } - return llvm::makeArrayRef(Args, ParsedArgs); + return llvm::ArrayRef(Args, ParsedArgs); } BlockCommandComment *Parser::parseBlockCommand() { @@ -334,7 +334,7 @@ BlockCommandComment *Parser::parseBlockCommand() { if (isTokBlockCommand()) { // Block command ahead. We can't nest block commands, so pretend that this // command has an empty argument. - ParagraphComment *Paragraph = S.actOnParagraphComment(None); + ParagraphComment *Paragraph = S.actOnParagraphComment(std::nullopt); if (PC) { S.actOnParamCommandFinish(PC, Paragraph); return PC; @@ -376,7 +376,7 @@ BlockCommandComment *Parser::parseBlockCommand() { ParagraphComment *Paragraph; if (EmptyParagraph) - Paragraph = S.actOnParagraphComment(None); + Paragraph = S.actOnParagraphComment(std::nullopt); else { BlockContentComment *Block = parseParagraphOrBlockCommand(); // Since we have checked for a block command, we should have parsed a @@ -467,16 +467,14 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() { } case tok::html_greater: - S.actOnHTMLStartTagFinish(HST, - S.copyArray(llvm::makeArrayRef(Attrs)), + S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)), Tok.getLocation(), /* IsSelfClosing = */ false); consumeToken(); return HST; case tok::html_slash_greater: - S.actOnHTMLStartTagFinish(HST, - S.copyArray(llvm::makeArrayRef(Attrs)), + S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)), Tok.getLocation(), /* IsSelfClosing = */ true); consumeToken(); @@ -494,16 +492,14 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() { Tok.is(tok::html_slash_greater)) continue; - S.actOnHTMLStartTagFinish(HST, - S.copyArray(llvm::makeArrayRef(Attrs)), + S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)), SourceLocation(), /* IsSelfClosing = */ false); return HST; default: // Not a token from an HTML start tag. Thus HTML tag prematurely ended. - S.actOnHTMLStartTagFinish(HST, - S.copyArray(llvm::makeArrayRef(Attrs)), + S.actOnHTMLStartTagFinish(HST, S.copyArray(llvm::ArrayRef(Attrs)), SourceLocation(), /* IsSelfClosing = */ false); bool StartLineInvalid; @@ -642,7 +638,7 @@ BlockContentComment *Parser::parseParagraphOrBlockCommand() { break; } - return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); + return S.actOnParagraphComment(S.copyArray(llvm::ArrayRef(Content))); } VerbatimBlockComment *Parser::parseVerbatimBlock() { @@ -679,14 +675,13 @@ VerbatimBlockComment *Parser::parseVerbatimBlock() { if (Tok.is(tok::verbatim_block_end)) { const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID()); - S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), - Info->Name, - S.copyArray(llvm::makeArrayRef(Lines))); + S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), Info->Name, + S.copyArray(llvm::ArrayRef(Lines))); consumeToken(); } else { // Unterminated \\verbatim block S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", - S.copyArray(llvm::makeArrayRef(Lines))); + S.copyArray(llvm::ArrayRef(Lines))); } return VB; @@ -762,7 +757,7 @@ FullComment *Parser::parseFullComment() { while (Tok.is(tok::newline)) consumeToken(); } - return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); + return S.actOnFullComment(S.copyArray(llvm::ArrayRef(Blocks))); } } // end namespace comments diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp index 9b0f03445888..a4250c0b7cbb 100644 --- a/clang/lib/AST/CommentSema.cpp +++ b/clang/lib/AST/CommentSema.cpp @@ -267,7 +267,7 @@ void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command, } auto *A = new (Allocator) Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg}; - Command->setArgs(llvm::makeArrayRef(A, 1)); + Command->setArgs(llvm::ArrayRef(A, 1)); } void Sema::actOnParamCommandFinish(ParamCommandComment *Command, @@ -303,7 +303,7 @@ void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command, auto *A = new (Allocator) Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg}; - Command->setArgs(llvm::makeArrayRef(A, 1)); + Command->setArgs(llvm::ArrayRef(A, 1)); if (!isTemplateOrSpecialization()) { // We already warned that this \\tparam is not attached to a template decl. @@ -314,7 +314,7 @@ void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command, ThisDeclInfo->TemplateParameters; SmallVector<unsigned, 2> Position; if (resolveTParamReference(Arg, TemplateParameters, &Position)) { - Command->setPosition(copyArray(llvm::makeArrayRef(Position))); + Command->setPosition(copyArray(llvm::ArrayRef(Position))); TParamCommandComment *&PrevCommand = TemplateParameterDocs[Arg]; if (PrevCommand) { SourceRange ArgRange(ArgLocBegin, ArgLocEnd); diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp index a42960ad3c7f..58411201c3b0 100644 --- a/clang/lib/AST/ComparisonCategories.cpp +++ b/clang/lib/AST/ComparisonCategories.cpp @@ -17,10 +17,11 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; -Optional<ComparisonCategoryType> +std::optional<ComparisonCategoryType> clang::getComparisonCategoryForBuiltinCmp(QualType T) { using CCT = ComparisonCategoryType; @@ -37,7 +38,7 @@ clang::getComparisonCategoryForBuiltinCmp(QualType T) { return CCT::StrongOrdering; // TODO: Extend support for operator<=> to ObjC types. - return llvm::None; + return std::nullopt; } bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const { diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 1f573346b441..eb9afbdb1c87 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -573,7 +573,7 @@ ExprDependence clang::computeDependence(RecoveryExpr *E) { // - type-dependent if we don't know the type (fallback to an opaque // dependent type), or the type is known and dependent, or it has // type-dependent subexpressions. - auto D = toExprDependenceForImpliedType(E->getType()->getDependence()) | + auto D = toExprDependenceAsWritten(E->getType()->getDependence()) | ExprDependence::ErrorDependent; // FIXME: remove the type-dependent bit from subexpressions, if the // RecoveryExpr has a non-dependent type. @@ -594,7 +594,7 @@ ExprDependence clang::computeDependence(PredefinedExpr *E) { ExprDependence clang::computeDependence(CallExpr *E, llvm::ArrayRef<Expr *> PreArgs) { auto D = E->getCallee()->getDependence(); - for (auto *A : llvm::makeArrayRef(E->getArgs(), E->getNumArgs())) { + for (auto *A : llvm::ArrayRef(E->getArgs(), E->getNumArgs())) { if (A) D |= A->getDependence(); } @@ -642,7 +642,7 @@ ExprDependence clang::computeDependence(InitListExpr *E) { ExprDependence clang::computeDependence(ShuffleVectorExpr *E) { auto D = toExprDependenceForImpliedType(E->getType()->getDependence()); - for (auto *C : llvm::makeArrayRef(E->getSubExprs(), E->getNumSubExprs())) + for (auto *C : llvm::ArrayRef(E->getSubExprs(), E->getNumSubExprs())) D |= C->getDependence(); return D; } @@ -686,7 +686,7 @@ ExprDependence clang::computeDependence(PseudoObjectExpr *O) { ExprDependence clang::computeDependence(AtomicExpr *A) { auto D = ExprDependence::None; - for (auto *E : llvm::makeArrayRef(A->getSubExprs(), A->getNumSubExprs())) + for (auto *E : llvm::ArrayRef(A->getSubExprs(), A->getNumSubExprs())) D |= E->getDependence(); return D; } @@ -831,6 +831,13 @@ ExprDependence clang::computeDependence(CXXFoldExpr *E) { return D; } +ExprDependence clang::computeDependence(CXXParenListInitExpr *E) { + auto D = ExprDependence::None; + for (const auto *A : E->getInitExprs()) + D |= A->getDependence(); + return D; +} + ExprDependence clang::computeDependence(TypeTraitExpr *E) { auto D = ExprDependence::None; for (const auto *A : E->getArgs()) @@ -853,7 +860,10 @@ ExprDependence clang::computeDependence(ConceptSpecializationExpr *E, ExprDependence D = ValueDependent ? ExprDependence::Value : ExprDependence::None; - return D | toExprDependence(TA); + auto Res = D | toExprDependence(TA); + if(!ValueDependent && E->getSatisfaction().ContainsErrors) + Res |= ExprDependence::Error; + return Res; } ExprDependence clang::computeDependence(ObjCArrayLiteral *E) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index aaba4345587b..e60cc28f6e0f 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -54,8 +54,6 @@ #include "clang/Basic/Visibility.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -69,6 +67,7 @@ #include <cstddef> #include <cstring> #include <memory> +#include <optional> #include <string> #include <tuple> #include <type_traits> @@ -170,8 +169,8 @@ withExplicitVisibilityAlready(LVComputationKind Kind) { return Kind; } -static Optional<Visibility> getExplicitVisibility(const NamedDecl *D, - LVComputationKind kind) { +static std::optional<Visibility> getExplicitVisibility(const NamedDecl *D, + LVComputationKind kind) { assert(!kind.IgnoreExplicitVisibility && "asking for explicit visibility when we shouldn't be"); return D->getExplicitVisibility(kind.getExplicitVisibilityKind()); @@ -187,8 +186,8 @@ static bool usesTypeVisibility(const NamedDecl *D) { /// Does the given declaration have member specialization information, /// and if so, is it an explicit specialization? -template <class T> static typename -std::enable_if<!std::is_base_of<RedeclarableTemplateDecl, T>::value, bool>::type +template <class T> +static std::enable_if_t<!std::is_base_of_v<RedeclarableTemplateDecl, T>, bool> isExplicitMemberSpecialization(const T *D) { if (const MemberSpecializationInfo *member = D->getMemberSpecializationInfo()) { @@ -220,8 +219,8 @@ static Visibility getVisibilityFromAttr(const T *attr) { } /// Return the explicit visibility of the given declaration. -static Optional<Visibility> getVisibilityOf(const NamedDecl *D, - NamedDecl::ExplicitVisibilityKind kind) { +static std::optional<Visibility> +getVisibilityOf(const NamedDecl *D, NamedDecl::ExplicitVisibilityKind kind) { // If we're ultimately computing the visibility of a type, look for // a 'type_visibility' attribute before looking for 'visibility'. if (kind == NamedDecl::VisibilityForType) { @@ -235,7 +234,7 @@ static Optional<Visibility> getVisibilityOf(const NamedDecl *D, return getVisibilityFromAttr(A); } - return None; + return std::nullopt; } LinkageInfo LinkageComputer::getLVForType(const Type &T, @@ -729,7 +728,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, LinkageInfo LV = getExternalLinkageFor(D); if (!hasExplicitVisibilityAlready(computation)) { - if (Optional<Visibility> Vis = getExplicitVisibility(D, computation)) { + if (std::optional<Visibility> Vis = getExplicitVisibility(D, computation)) { LV.mergeVisibility(*Vis, true); } else { // If we're declared in a namespace with a visibility attribute, @@ -739,7 +738,8 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, DC = DC->getParent()) { const auto *ND = dyn_cast<NamespaceDecl>(DC); if (!ND) continue; - if (Optional<Visibility> Vis = getExplicitVisibility(ND, computation)) { + if (std::optional<Visibility> Vis = + getExplicitVisibility(ND, computation)) { LV.mergeVisibility(*Vis, true); break; } @@ -834,6 +834,15 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D, if (Function->getStorageClass() == SC_PrivateExtern) LV.mergeVisibility(HiddenVisibility, true); + // OpenMP target declare device functions are not callable from the host so + // they should not be exported from the device image. This applies to all + // functions as the host-callable kernel functions are emitted at codegen. + if (Context.getLangOpts().OpenMP && Context.getLangOpts().OpenMPIsDevice && + ((Context.getTargetInfo().getTriple().isAMDGPU() || + Context.getTargetInfo().getTriple().isNVPTX()) || + OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(Function))) + LV.mergeVisibility(HiddenVisibility, /*newExplicit=*/false); + // Note that Sema::MergeCompatibleFunctionDecls already takes care of // merging storage classes and visibility attributes, so we don't have to // look at previous decls in here. @@ -956,7 +965,7 @@ LinkageComputer::getLVForClassMember(const NamedDecl *D, // If we have an explicit visibility attribute, merge that in. if (!hasExplicitVisibilityAlready(computation)) { - if (Optional<Visibility> Vis = getExplicitVisibility(D, computation)) + if (std::optional<Visibility> Vis = getExplicitVisibility(D, computation)) LV.mergeVisibility(*Vis, true); // If we're paying attention to global visibility, apply // -finline-visibility-hidden if this is an inline method. @@ -1012,6 +1021,16 @@ LinkageComputer::getLVForClassMember(const NamedDecl *D, explicitSpecSuppressor = MD; } + // OpenMP target declare device functions are not callable from the host so + // they should not be exported from the device image. This applies to all + // functions as the host-callable kernel functions are emitted at codegen. + ASTContext &Context = D->getASTContext(); + if (Context.getLangOpts().OpenMP && Context.getLangOpts().OpenMPIsDevice && + ((Context.getTargetInfo().getTriple().isAMDGPU() || + Context.getTargetInfo().getTriple().isNVPTX()) || + OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(MD))) + LV.mergeVisibility(HiddenVisibility, /*newExplicit=*/false); + } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { if (const auto *spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { mergeTemplateLV(LV, spec, computation); @@ -1158,14 +1177,14 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const { return LinkageComputer{}.getDeclLinkageAndVisibility(this); } -static Optional<Visibility> +static std::optional<Visibility> getExplicitVisibilityAux(const NamedDecl *ND, NamedDecl::ExplicitVisibilityKind kind, bool IsMostRecent) { assert(!IsMostRecent || ND == ND->getMostRecentDecl()); // Check the declaration itself first. - if (Optional<Visibility> V = getVisibilityOf(ND, kind)) + if (std::optional<Visibility> V = getVisibilityOf(ND, kind)) return V; // If this is a member class of a specialization of a class template @@ -1185,11 +1204,11 @@ getExplicitVisibilityAux(const NamedDecl *ND, const auto *TD = spec->getSpecializedTemplate()->getTemplatedDecl(); while (TD != nullptr) { auto Vis = getVisibilityOf(TD, kind); - if (Vis != None) + if (Vis != std::nullopt) return Vis; TD = TD->getPreviousDecl(); } - return None; + return std::nullopt; } // Use the most recent declaration. @@ -1210,7 +1229,7 @@ getExplicitVisibilityAux(const NamedDecl *ND, return getVisibilityOf(VTSD->getSpecializedTemplate()->getTemplatedDecl(), kind); - return None; + return std::nullopt; } // Also handle function template specializations. if (const auto *fn = dyn_cast<FunctionDecl>(ND)) { @@ -1227,17 +1246,17 @@ getExplicitVisibilityAux(const NamedDecl *ND, if (InstantiatedFrom) return getVisibilityOf(InstantiatedFrom, kind); - return None; + return std::nullopt; } // The visibility of a template is stored in the templated decl. if (const auto *TD = dyn_cast<TemplateDecl>(ND)) return getVisibilityOf(TD->getTemplatedDecl(), kind); - return None; + return std::nullopt; } -Optional<Visibility> +std::optional<Visibility> NamedDecl::getExplicitVisibility(ExplicitVisibilityKind kind) const { return getExplicitVisibilityAux(this, kind, false); } @@ -1252,8 +1271,13 @@ LinkageInfo LinkageComputer::getLVForClosure(const DeclContext *DC, else if (isa<ParmVarDecl>(ContextDecl)) Owner = dyn_cast<NamedDecl>(ContextDecl->getDeclContext()->getRedeclContext()); - else + else if (isa<ImplicitConceptSpecializationDecl>(ContextDecl)) { + // Replace with the concept's owning decl, which is either a namespace or a + // TU, so this needs a dyn_cast. + Owner = dyn_cast<NamedDecl>(ContextDecl->getDeclContext()); + } else { Owner = cast<NamedDecl>(ContextDecl); + } if (!Owner) return LinkageInfo::none(); @@ -1289,7 +1313,7 @@ LinkageInfo LinkageComputer::getLVForLocalDecl(const NamedDecl *D, LinkageInfo LV; if (!hasExplicitVisibilityAlready(computation)) { - if (Optional<Visibility> Vis = + if (std::optional<Visibility> Vis = getExplicitVisibility(Function, computation)) LV.mergeVisibility(*Vis, true); } @@ -1310,7 +1334,8 @@ LinkageInfo LinkageComputer::getLVForLocalDecl(const NamedDecl *D, if (Var->getStorageClass() == SC_PrivateExtern) LV.mergeVisibility(HiddenVisibility, true); else if (!hasExplicitVisibilityAlready(computation)) { - if (Optional<Visibility> Vis = getExplicitVisibility(Var, computation)) + if (std::optional<Visibility> Vis = + getExplicitVisibility(Var, computation)) LV.mergeVisibility(*Vis, true); } @@ -1505,7 +1530,7 @@ LinkageInfo LinkageComputer::getLVForDecl(const NamedDecl *D, if (computation.IgnoreAllVisibility && D->hasCachedLinkage()) return LinkageInfo(D->getCachedLinkage(), DefaultVisibility, false); - if (llvm::Optional<LinkageInfo> LI = lookup(D, computation)) + if (std::optional<LinkageInfo> LI = lookup(D, computation)) return *LI; LinkageInfo LV = computeLVForDecl(D, computation); @@ -1527,7 +1552,7 @@ LinkageInfo LinkageComputer::getLVForDecl(const NamedDecl *D, // that all other computed linkages match, check that the one we just // computed also does. NamedDecl *Old = nullptr; - for (auto I : D->redecls()) { + for (auto *I : D->redecls()) { auto *T = cast<NamedDecl>(I); if (T == D) continue; @@ -1601,8 +1626,12 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const { llvm_unreachable("unknown module kind"); } -void NamedDecl::printName(raw_ostream &os) const { - os << Name; +void NamedDecl::printName(raw_ostream &OS, const PrintingPolicy&) const { + OS << Name; +} + +void NamedDecl::printName(raw_ostream &OS) const { + printName(OS, getASTContext().getPrintingPolicy()); } std::string NamedDecl::getQualifiedNameAsString() const { @@ -1620,7 +1649,7 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P) const { if (getDeclContext()->isFunctionOrMethod()) { // We do not print '(anonymous)' for function parameters without name. - printName(OS); + printName(OS, P); return; } printNestedNameSpecifier(OS, P); @@ -1631,7 +1660,7 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, // fall back to "(anonymous)". SmallString<64> NameBuffer; llvm::raw_svector_ostream NameOS(NameBuffer); - printName(NameOS); + printName(NameOS, P); if (NameBuffer.empty()) OS << "(anonymous)"; else @@ -1754,7 +1783,7 @@ void NamedDecl::getNameForDiagnostic(raw_ostream &OS, if (Qualified) printQualifiedName(OS, Policy); else - printName(OS); + printName(OS, Policy); } template<typename T> static bool isRedeclarableImpl(Redeclarable<T> *) { @@ -1825,7 +1854,7 @@ bool NamedDecl::declarationReplaces(NamedDecl *OldD, bool IsKnownNewer) const { // Check whether this is actually newer than OldD. We want to keep the // newer declaration. This loop will usually only iterate once, because // OldD is usually the previous declaration. - for (auto D : redecls()) { + for (auto *D : redecls()) { if (D == OldD) break; @@ -2273,7 +2302,7 @@ VarDecl *VarDecl::getActingDefinition() { VarDecl *VarDecl::getDefinition(ASTContext &C) { VarDecl *First = getFirstDecl(); - for (auto I : First->redecls()) { + for (auto *I : First->redecls()) { if (I->isThisDeclarationADefinition(C) == Definition) return I; } @@ -2284,7 +2313,7 @@ VarDecl::DefinitionKind VarDecl::hasDefinition(ASTContext &C) const { DefinitionKind Kind = DeclarationOnly; const VarDecl *First = getFirstDecl(); - for (auto I : First->redecls()) { + for (auto *I : First->redecls()) { Kind = std::max(Kind, I->isThisDeclarationADefinition(C)); if (Kind == Definition) break; @@ -2294,7 +2323,7 @@ VarDecl::DefinitionKind VarDecl::hasDefinition(ASTContext &C) const { } const Expr *VarDecl::getAnyInitializer(const VarDecl *&D) const { - for (auto I : redecls()) { + for (auto *I : redecls()) { if (auto Expr = I->getInit()) { D = I; return Expr; @@ -2330,7 +2359,7 @@ Stmt **VarDecl::getInitAddress() { VarDecl *VarDecl::getInitializingDeclaration() { VarDecl *Def = nullptr; - for (auto I : redecls()) { + for (auto *I : redecls()) { if (I->hasInit()) return I; @@ -2580,7 +2609,7 @@ bool VarDecl::isNonEscapingByref() const { bool VarDecl::hasDependentAlignment() const { QualType T = getType(); - return T->isDependentType() || T->isUndeducedAutoType() || + return T->isDependentType() || T->isUndeducedType() || llvm::any_of(specific_attrs<AlignedAttr>(), [](const AlignedAttr *AA) { return AA->isAlignmentDependent(); }); @@ -2870,8 +2899,7 @@ Expr *ParmVarDecl::getDefaultArg() { Expr *Arg = getInit(); if (auto *E = dyn_cast_or_null<FullExpr>(Arg)) - if (!isa<ConstantExpr>(E)) - return E->getSubExpr(); + return E->getSubExpr(); return Arg; } @@ -2970,6 +2998,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.IsMultiVersion = false; FunctionDeclBits.IsCopyDeductionCandidate = false; FunctionDeclBits.HasODRHash = false; + FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false; if (TrailingRequiresClause) setTrailingRequiresClause(TrailingRequiresClause); } @@ -3015,7 +3044,7 @@ FunctionDecl::getDefaultedFunctionInfo() const { } bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const { - for (auto I : redecls()) { + for (auto *I : redecls()) { if (I->doesThisDeclarationHaveABody()) { Definition = I; return true; @@ -3162,11 +3191,13 @@ bool FunctionDecl::isMSVCRTEntryPoint() const { } bool FunctionDecl::isReservedGlobalPlacementOperator() const { - assert(getDeclName().getNameKind() == DeclarationName::CXXOperatorName); - assert(getDeclName().getCXXOverloadedOperator() == OO_New || - getDeclName().getCXXOverloadedOperator() == OO_Delete || - getDeclName().getCXXOverloadedOperator() == OO_Array_New || - getDeclName().getCXXOverloadedOperator() == OO_Array_Delete); + if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName) + return false; + if (getDeclName().getCXXOverloadedOperator() != OO_New && + getDeclName().getCXXOverloadedOperator() != OO_Delete && + getDeclName().getCXXOverloadedOperator() != OO_Array_New && + getDeclName().getCXXOverloadedOperator() != OO_Array_Delete) + return false; if (!getDeclContext()->getRedeclContext()->isTranslationUnit()) return false; @@ -3185,7 +3216,7 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const { } bool FunctionDecl::isReplaceableGlobalAllocationFunction( - Optional<unsigned> *AlignmentParam, bool *IsNothrow) const { + std::optional<unsigned> *AlignmentParam, bool *IsNothrow) const { if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName) return false; if (getDeclName().getCXXOverloadedOperator() != OO_New && @@ -3329,6 +3360,8 @@ bool FunctionDecl::isNoReturn() const { MultiVersionKind FunctionDecl::getMultiVersionKind() const { if (hasAttr<TargetAttr>()) return MultiVersionKind::Target; + if (hasAttr<TargetVersionAttr>()) + return MultiVersionKind::TargetVersion; if (hasAttr<CPUDispatchAttr>()) return MultiVersionKind::CPUDispatch; if (hasAttr<CPUSpecificAttr>()) @@ -3347,7 +3380,8 @@ bool FunctionDecl::isCPUSpecificMultiVersion() const { } bool FunctionDecl::isTargetMultiVersion() const { - return isMultiVersion() && hasAttr<TargetAttr>(); + return isMultiVersion() && + (hasAttr<TargetAttr>() || hasAttr<TargetVersionAttr>()); } bool FunctionDecl::isTargetClonesMultiVersion() const { @@ -3486,8 +3520,8 @@ unsigned FunctionDecl::getMinRequiredArguments() const { bool FunctionDecl::hasOneParamOrDefaultArgs() const { return getNumParams() == 1 || (getNumParams() > 1 && - std::all_of(param_begin() + 1, param_end(), - [](ParmVarDecl *P) { return P->hasDefaultArg(); })); + llvm::all_of(llvm::drop_begin(parameters()), + [](ParmVarDecl *P) { return P->hasDefaultArg(); })); } /// The combination of the extern and inline keywords under MSVC forces @@ -3686,7 +3720,7 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { // If any declaration is 'inline' but not 'extern', then this definition // is externally visible. - for (auto Redecl : redecls()) { + for (auto *Redecl : redecls()) { if (Redecl->isInlineSpecified() && Redecl->getStorageClass() != SC_Extern) return true; @@ -3703,7 +3737,7 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { // [...] If all of the file scope declarations for a function in a // translation unit include the inline function specifier without extern, // then the definition in that translation unit is an inline definition. - for (auto Redecl : redecls()) { + for (auto *Redecl : redecls()) { if (RedeclForcesDefC99(Redecl)) return true; } @@ -4408,7 +4442,7 @@ void TagDecl::startDefinition() { if (auto *D = dyn_cast<CXXRecordDecl>(this)) { struct CXXRecordDecl::DefinitionData *Data = new (getASTContext()) struct CXXRecordDecl::DefinitionData(D); - for (auto I : redecls()) + for (auto *I : redecls()) cast<CXXRecordDecl>(I)->DefinitionData = Data; } } @@ -4441,7 +4475,7 @@ TagDecl *TagDecl::getDefinition() const { if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(this)) return CXXRD->getDefinition(); - for (auto R : redecls()) + for (auto *R : redecls()) if (R->isCompleteDefinition()) return R; @@ -4468,6 +4502,23 @@ void TagDecl::setQualifierInfo(NestedNameSpecifierLoc QualifierLoc) { } } +void TagDecl::printName(raw_ostream &OS, const PrintingPolicy &Policy) const { + DeclarationName Name = getDeclName(); + // If the name is supposed to have an identifier but does not have one, then + // the tag is anonymous and we should print it differently. + if (Name.isIdentifier() && !Name.getAsIdentifierInfo()) { + // If the caller wanted to print a qualified name, they've already printed + // the scope. And if the caller doesn't want that, the scope information + // is already printed as part of the type. + PrintingPolicy Copy(Policy); + Copy.SuppressScope = true; + getASTContext().getTagDeclType(this).print(OS, Copy); + return; + } + // Otherwise, do the normal printing. + Name.print(OS, Policy); +} + void TagDecl::setTemplateParameterListsInfo( ASTContext &Context, ArrayRef<TemplateParameterList *> TPLists) { assert(!TPLists.empty()); @@ -4621,6 +4672,21 @@ SourceRange EnumDecl::getSourceRange() const { return Res; } +void EnumDecl::getValueRange(llvm::APInt &Max, llvm::APInt &Min) const { + unsigned Bitwidth = getASTContext().getIntWidth(getIntegerType()); + unsigned NumNegativeBits = getNumNegativeBits(); + unsigned NumPositiveBits = getNumPositiveBits(); + + if (NumNegativeBits) { + unsigned NumBits = std::max(NumNegativeBits, NumPositiveBits + 1); + Max = llvm::APInt(Bitwidth, 1) << (NumBits - 1); + Min = -Max; + } else { + Max = llvm::APInt(Bitwidth, 1) << NumPositiveBits; + Min = llvm::APInt::getZero(Bitwidth); + } +} + //===----------------------------------------------------------------------===// // RecordDecl Implementation //===----------------------------------------------------------------------===// @@ -4645,6 +4711,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); setIsRandomized(false); + setODRHash(0); } RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC, @@ -4819,6 +4886,19 @@ const FieldDecl *RecordDecl::findFirstNamedDataMember() const { return nullptr; } +unsigned RecordDecl::getODRHash() { + if (hasODRHash()) + return RecordDeclBits.ODRHash; + + // Only calculate hash on first call of getODRHash per record. + ODRHash Hash; + Hash.AddRecordDecl(this); + // For RecordDecl the ODRHash is stored in the remaining 26 + // bit of RecordDeclBits, adjust the hash to accomodate. + setODRHash(Hash.CalculateHash() >> 6); + return RecordDeclBits.ODRHash; +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// @@ -4968,6 +5048,12 @@ bool ValueDecl::isWeak() const { MostRecent->hasAttr<WeakRefAttr>() || isWeakImported(); } +bool ValueDecl::isInitCapture() const { + if (auto *Var = llvm::dyn_cast<VarDecl>(this)) + return Var->isInitCapture(); + return false; +} + void ImplicitParamDecl::anchor() {} ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, @@ -5073,8 +5159,9 @@ IndirectFieldDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, IndirectFieldDecl *IndirectFieldDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) IndirectFieldDecl(C, nullptr, SourceLocation(), - DeclarationName(), QualType(), None); + return new (C, ID) + IndirectFieldDecl(C, nullptr, SourceLocation(), DeclarationName(), + QualType(), std::nullopt); } SourceRange EnumConstantDecl::getSourceRange() const { @@ -5179,6 +5266,29 @@ FileScopeAsmDecl *FileScopeAsmDecl::CreateDeserialized(ASTContext &C, SourceLocation()); } +void TopLevelStmtDecl::anchor() {} + +TopLevelStmtDecl *TopLevelStmtDecl::Create(ASTContext &C, Stmt *Statement) { + assert(Statement); + assert(C.getLangOpts().IncrementalExtensions && + "Must be used only in incremental mode"); + + SourceLocation BeginLoc = Statement->getBeginLoc(); + DeclContext *DC = C.getTranslationUnitDecl(); + + return new (C, DC) TopLevelStmtDecl(DC, BeginLoc, Statement); +} + +TopLevelStmtDecl *TopLevelStmtDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) + TopLevelStmtDecl(/*DC=*/nullptr, SourceLocation(), /*S=*/nullptr); +} + +SourceRange TopLevelStmtDecl::getSourceRange() const { + return SourceRange(getLocation(), Statement->getEndLoc()); +} + void EmptyDecl::anchor() {} EmptyDecl *EmptyDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { @@ -5189,6 +5299,40 @@ EmptyDecl *EmptyDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) EmptyDecl(nullptr, SourceLocation()); } +HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *ID, + SourceLocation IDLoc, SourceLocation LBrace) + : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), + DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), + IsCBuffer(CBuffer) {} + +HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, + DeclContext *LexicalParent, bool CBuffer, + SourceLocation KwLoc, IdentifierInfo *ID, + SourceLocation IDLoc, + SourceLocation LBrace) { + // For hlsl like this + // cbuffer A { + // cbuffer B { + // } + // } + // compiler should treat it as + // cbuffer A { + // } + // cbuffer B { + // } + // FIXME: support nested buffers if required for back-compat. + DeclContext *DC = LexicalParent; + HLSLBufferDecl *Result = + new (C, DC) HLSLBufferDecl(DC, CBuffer, KwLoc, ID, IDLoc, LBrace); + return Result; +} + +HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, + SourceLocation(), SourceLocation()); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// @@ -5248,11 +5392,11 @@ ImportDecl *ImportDecl::CreateDeserialized(ASTContext &C, unsigned ID, ArrayRef<SourceLocation> ImportDecl::getIdentifierLocs() const { if (!isImportComplete()) - return None; + return std::nullopt; const auto *StoredLocs = getTrailingObjects<SourceLocation>(); - return llvm::makeArrayRef(StoredLocs, - getNumModuleIdentifiers(getImportedModule())); + return llvm::ArrayRef(StoredLocs, + getNumModuleIdentifiers(getImportedModule())); } SourceRange ImportDecl::getSourceRange() const { diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index d12330de1500..c94fc602155b 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -152,6 +152,15 @@ void Decl::setInvalidDecl(bool Invalid) { } } +bool DeclContext::hasValidDeclKind() const { + switch (getDeclKind()) { +#define DECL(DERIVED, BASE) case Decl::DERIVED: return true; +#define ABSTRACT_DECL(DECL) +#include "clang/AST/DeclNodes.inc" + } + return false; +} + const char *DeclContext::getDeclKindName() const { switch (getDeclKind()) { #define DECL(DERIVED, BASE) case Decl::DERIVED: return #DERIVED; @@ -252,12 +261,12 @@ const TemplateParameterList *Decl::getDescribedTemplateParams() const { bool Decl::isTemplated() const { // A declaration is templated if it is a template or a template pattern, or - // is within (lexcially for a friend, semantically otherwise) a dependent - // context. - // FIXME: Should local extern declarations be treated like friends? + // is within (lexcially for a friend or local function declaration, + // semantically otherwise) a dependent context. if (auto *AsDC = dyn_cast<DeclContext>(this)) return AsDC->isDependentContext(); - auto *DC = getFriendObjectKind() ? getLexicalDeclContext() : getDeclContext(); + auto *DC = getFriendObjectKind() || isLocalExternDecl() + ? getLexicalDeclContext() : getDeclContext(); return DC->isDependentContext() || isTemplateDecl() || getDescribedTemplateParams(); } @@ -286,8 +295,7 @@ unsigned Decl::getTemplateDepth() const { const DeclContext *Decl::getParentFunctionOrMethod(bool LexicalParent) const { for (const DeclContext *DC = LexicalParent ? getLexicalDeclContext() : getDeclContext(); - DC && !DC->isTranslationUnit() && !DC->isNamespace(); - DC = DC->getParent()) + DC && !DC->isFileContext(); DC = DC->getParent()) if (DC->isFunctionOrMethod()) return DC; @@ -397,6 +405,11 @@ bool Decl::isInStdNamespace() const { return DC && DC->isStdNamespace(); } +bool Decl::isFileContextDecl() const { + const auto *DC = dyn_cast<DeclContext>(this); + return DC && DC->isFileContext(); +} + TranslationUnitDecl *Decl::getTranslationUnitDecl() { if (auto *TUD = dyn_cast<TranslationUnitDecl>(this)) return TUD; @@ -750,6 +763,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ObjCMethod: case ObjCProperty: case MSProperty: + case HLSLBuffer: return IDNS_Ordinary; case Label: return IDNS_Label; @@ -829,6 +843,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case LinkageSpec: case Export: case FileScopeAsm: + case TopLevelStmt: case StaticAssert: case ObjCPropertyImpl: case PragmaComment: @@ -860,6 +875,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Empty: case LifetimeExtendedTemporary: case RequiresExprBody: + case ImplicitConceptSpecialization: // Never looked up by name. return 0; } @@ -1032,6 +1048,11 @@ const FunctionType *Decl::getFunctionType(bool BlocksToo) const { return Ty->getAs<FunctionType>(); } +DeclContext *Decl::getNonTransparentDeclContext() { + assert(getDeclContext()); + return getDeclContext()->getNonTransparentContext(); +} + /// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). template <class T> static Decl *getNonClosureContext(T *D) { @@ -1188,7 +1209,7 @@ bool DeclContext::isTransparentContext() const { if (getDeclKind() == Decl::Enum) return !cast<EnumDecl>(this)->isScoped(); - return getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export; + return isa<LinkageSpecDecl, ExportDecl, HLSLBufferDecl>(this); } static bool isLinkageSpecContext(const DeclContext *DC, @@ -1253,6 +1274,15 @@ DeclContext *DeclContext::getPrimaryContext() { // There is only one DeclContext for these entities. return this; + case Decl::HLSLBuffer: + // Each buffer, even with the same name, is a distinct construct. + // Multiple buffers with the same name are allowed for backward + // compatibility. + // As long as buffers have unique resource bindings the names don't matter. + // The names get exposed via the CPU-side reflection API which + // supports querying bindings, so we cannot remove them. + return this; + case Decl::TranslationUnit: return static_cast<TranslationUnitDecl *>(this)->getFirstDecl(); case Decl::Namespace: @@ -1766,7 +1796,8 @@ void DeclContext::localUncachedLookup(DeclarationName Name, if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage() && Name) { lookup_result LookupResults = lookup(Name); Results.insert(Results.end(), LookupResults.begin(), LookupResults.end()); - return; + if (!Results.empty()) + return; } // If we have a lookup table, check there first. Maybe we'll get lucky. diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index c307cbe02ecf..3cf355714107 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -36,7 +36,7 @@ #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" -#include "llvm/ADT/None.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" @@ -768,12 +768,16 @@ void CXXRecordDecl::addedMember(Decl *D) { // Note that we have a user-declared constructor. data().UserDeclaredConstructor = true; - // C++ [class]p4: - // A POD-struct is an aggregate class [...] - // Since the POD bit is meant to be C++03 POD-ness, clear it even if - // the type is technically an aggregate in C++0x since it wouldn't be - // in 03. - data().PlainOldData = false; + const TargetInfo &TI = getASTContext().getTargetInfo(); + if ((!Constructor->isDeleted() && !Constructor->isDefaulted()) || + !TI.areDefaultedSMFStillPOD(getLangOpts())) { + // C++ [class]p4: + // A POD-struct is an aggregate class [...] + // Since the POD bit is meant to be C++03 POD-ness, clear it even if + // the type is technically an aggregate in C++0x since it wouldn't be + // in 03. + data().PlainOldData = false; + } } if (Constructor->isDefaultConstructor()) { @@ -881,22 +885,30 @@ void CXXRecordDecl::addedMember(Decl *D) { if (!Method->isImplicit()) { data().UserDeclaredSpecialMembers |= SMKind; - // C++03 [class]p4: - // A POD-struct is an aggregate class that has [...] no user-defined - // copy assignment operator and no user-defined destructor. - // - // Since the POD bit is meant to be C++03 POD-ness, and in C++03, - // aggregates could not have any constructors, clear it even for an - // explicitly defaulted or deleted constructor. - // type is technically an aggregate in C++0x since it wouldn't be in 03. - // - // Also, a user-declared move assignment operator makes a class non-POD. - // This is an extension in C++03. - data().PlainOldData = false; + const TargetInfo &TI = getASTContext().getTargetInfo(); + if ((!Method->isDeleted() && !Method->isDefaulted() && + SMKind != SMF_MoveAssignment) || + !TI.areDefaultedSMFStillPOD(getLangOpts())) { + // C++03 [class]p4: + // A POD-struct is an aggregate class that has [...] no user-defined + // copy assignment operator and no user-defined destructor. + // + // Since the POD bit is meant to be C++03 POD-ness, and in C++03, + // aggregates could not have any constructors, clear it even for an + // explicitly defaulted or deleted constructor. + // type is technically an aggregate in C++0x since it wouldn't be in + // 03. + // + // Also, a user-declared move assignment operator makes a class + // non-POD. This is an extension in C++03. + data().PlainOldData = false; + } } - // We delay updating destructor relevant properties until - // addedSelectedDestructor. - // FIXME: Defer this for the other special member functions as well. + // When instantiating a class, we delay updating the destructor and + // triviality properties of the class until selecting a destructor and + // computing the eligibility of its special member functions. This is + // because there might be function constraints that we need to evaluate + // and compare later in the instantiation. if (!Method->isIneligibleOrNotSelected()) { addedEligibleSpecialMemberFunction(Method, SMKind); } @@ -1365,7 +1377,11 @@ void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) { } void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD, - unsigned SMKind) { + unsigned SMKind) { + // FIXME: We shouldn't change DeclaredNonTrivialSpecialMembers if `MD` is + // a function template, but this needs CWG attention before we break ABI. + // See https://github.com/llvm/llvm-project/issues/59206 + if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) { if (DD->isUserProvided()) data().HasIrrelevantDestructor = false; @@ -1437,10 +1453,21 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) { // Update which trivial / non-trivial special members we have. // addedMember will have skipped this step for this member. - if (D->isTrivial()) - data().HasTrivialSpecialMembers |= SMKind; - else - data().DeclaredNonTrivialSpecialMembers |= SMKind; + if (!D->isIneligibleOrNotSelected()) { + if (D->isTrivial()) + data().HasTrivialSpecialMembers |= SMKind; + else + data().DeclaredNonTrivialSpecialMembers |= SMKind; + } +} + +void CXXRecordDecl::LambdaDefinitionData::AddCaptureList(ASTContext &Ctx, + Capture *CaptureList) { + Captures.push_back(CaptureList); + if (Captures.size() == 2) { + // The TinyPtrVector member now needs destruction. + Ctx.addDestruction(&Captures); + } } void CXXRecordDecl::setCaptures(ASTContext &Context, @@ -1450,9 +1477,9 @@ void CXXRecordDecl::setCaptures(ASTContext &Context, // Copy captures. Data.NumCaptures = Captures.size(); Data.NumExplicitCaptures = 0; - Data.Captures = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) * - Captures.size()); - LambdaCapture *ToCapture = Data.Captures; + auto *ToCapture = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) * + Captures.size()); + Data.AddCaptureList(Context, ToCapture); for (unsigned I = 0, N = Captures.size(); I != N; ++I) { if (Captures[I].isExplicit()) ++Data.NumExplicitCaptures; @@ -1570,21 +1597,23 @@ CXXMethodDecl *CXXRecordDecl::getLambdaStaticInvoker(CallingConv CC) const { } void CXXRecordDecl::getCaptureFields( - llvm::DenseMap<const VarDecl *, FieldDecl *> &Captures, - FieldDecl *&ThisCapture) const { + llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures, + FieldDecl *&ThisCapture) const { Captures.clear(); ThisCapture = nullptr; LambdaDefinitionData &Lambda = getLambdaData(); - RecordDecl::field_iterator Field = field_begin(); - for (const LambdaCapture *C = Lambda.Captures, *CEnd = C + Lambda.NumCaptures; - C != CEnd; ++C, ++Field) { - if (C->capturesThis()) - ThisCapture = *Field; - else if (C->capturesVariable()) - Captures[C->getCapturedVar()] = *Field; + for (const LambdaCapture *List : Lambda.Captures) { + RecordDecl::field_iterator Field = field_begin(); + for (const LambdaCapture *C = List, *CEnd = C + Lambda.NumCaptures; + C != CEnd; ++C, ++Field) { + if (C->capturesThis()) + ThisCapture = *Field; + else if (C->capturesVariable()) + Captures[C->getCapturedVar()] = *Field; + } + assert(Field == field_end()); } - assert(Field == field_end()); } TemplateParameterList * @@ -1608,7 +1637,7 @@ CXXRecordDecl::getLambdaExplicitTemplateParameters() const { const auto ExplicitEnd = llvm::partition_point( *List, [](const NamedDecl *D) { return !D->isImplicit(); }); - return llvm::makeArrayRef(List->begin(), ExplicitEnd); + return llvm::ArrayRef(List->begin(), ExplicitEnd); } Decl *CXXRecordDecl::getLambdaContextDecl() const { @@ -2566,7 +2595,7 @@ SourceLocation CXXCtorInitializer::getSourceLocation() const { return getMemberLocation(); if (const auto *TSInfo = Initializee.get<TypeSourceInfo *>()) - return TSInfo->getTypeLoc().getLocalSourceRange().getBegin(); + return TSInfo->getTypeLoc().getBeginLoc(); return {}; } @@ -2865,41 +2894,47 @@ NamespaceDecl *UsingDirectiveDecl::getNominatedNamespace() { NamespaceDecl::NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, - IdentifierInfo *Id, NamespaceDecl *PrevDecl) + IdentifierInfo *Id, NamespaceDecl *PrevDecl, + bool Nested) : NamedDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace), - redeclarable_base(C), LocStart(StartLoc), - AnonOrFirstNamespaceAndInline(nullptr, Inline) { + redeclarable_base(C), LocStart(StartLoc) { + unsigned Flags = 0; + if (Inline) + Flags |= F_Inline; + if (Nested) + Flags |= F_Nested; + AnonOrFirstNamespaceAndFlags = {nullptr, Flags}; setPreviousDecl(PrevDecl); if (PrevDecl) - AnonOrFirstNamespaceAndInline.setPointer(PrevDecl->getOriginalNamespace()); + AnonOrFirstNamespaceAndFlags.setPointer(PrevDecl->getOriginalNamespace()); } NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, - NamespaceDecl *PrevDecl) { - return new (C, DC) NamespaceDecl(C, DC, Inline, StartLoc, IdLoc, Id, - PrevDecl); + NamespaceDecl *PrevDecl, bool Nested) { + return new (C, DC) + NamespaceDecl(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, Nested); } NamespaceDecl *NamespaceDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) NamespaceDecl(C, nullptr, false, SourceLocation(), - SourceLocation(), nullptr, nullptr); + SourceLocation(), nullptr, nullptr, false); } NamespaceDecl *NamespaceDecl::getOriginalNamespace() { if (isFirstDecl()) return this; - return AnonOrFirstNamespaceAndInline.getPointer(); + return AnonOrFirstNamespaceAndFlags.getPointer(); } const NamespaceDecl *NamespaceDecl::getOriginalNamespace() const { if (isFirstDecl()) return this; - return AnonOrFirstNamespaceAndInline.getPointer(); + return AnonOrFirstNamespaceAndFlags.getPointer(); } bool NamespaceDecl::isOriginalNamespace() const { return isFirstDecl(); } @@ -3087,18 +3122,23 @@ SourceRange UsingDecl::getSourceRange() const { void UsingEnumDecl::anchor() {} UsingEnumDecl *UsingEnumDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation UL, SourceLocation EL, - SourceLocation NL, EnumDecl *Enum) { - return new (C, DC) UsingEnumDecl(DC, Enum->getDeclName(), UL, EL, NL, Enum); + SourceLocation UL, + SourceLocation EL, + SourceLocation NL, + TypeSourceInfo *EnumType) { + assert(isa<EnumDecl>(EnumType->getType()->getAsTagDecl())); + return new (C, DC) + UsingEnumDecl(DC, EnumType->getType()->getAsTagDecl()->getDeclName(), UL, EL, NL, EnumType); } UsingEnumDecl *UsingEnumDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) UsingEnumDecl(nullptr, DeclarationName(), SourceLocation(), - SourceLocation(), SourceLocation(), nullptr); + return new (C, ID) + UsingEnumDecl(nullptr, DeclarationName(), SourceLocation(), + SourceLocation(), SourceLocation(), nullptr); } SourceRange UsingEnumDecl::getSourceRange() const { - return SourceRange(EnumLocation, getLocation()); + return SourceRange(UsingLocation, EnumType->getTypeLoc().getEndLoc()); } void UsingPackDecl::anchor() {} @@ -3113,7 +3153,8 @@ UsingPackDecl *UsingPackDecl::Create(ASTContext &C, DeclContext *DC, UsingPackDecl *UsingPackDecl::CreateDeserialized(ASTContext &C, unsigned ID, unsigned NumExpansions) { size_t Extra = additionalSizeToAlloc<NamedDecl *>(NumExpansions); - auto *Result = new (C, ID, Extra) UsingPackDecl(nullptr, nullptr, None); + auto *Result = + new (C, ID, Extra) UsingPackDecl(nullptr, nullptr, std::nullopt); Result->NumExpansions = NumExpansions; auto *Trail = Result->getTrailingObjects<NamedDecl *>(); for (unsigned I = 0; I != NumExpansions; ++I) @@ -3208,6 +3249,16 @@ StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, nullptr, SourceLocation(), false); } +VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() { + assert((isa<VarDecl, BindingDecl>(this)) && + "expected a VarDecl or a BindingDecl"); + if (auto *Var = llvm::dyn_cast<VarDecl>(this)) + return Var; + if (auto *BD = llvm::dyn_cast<BindingDecl>(this)) + return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl()); + return nullptr; +} + void BindingDecl::anchor() {} BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC, @@ -3251,7 +3302,7 @@ DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C, size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings); auto *Result = new (C, ID, Extra) DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(), - QualType(), nullptr, StorageClass(), None); + QualType(), nullptr, StorageClass(), std::nullopt); // Set up and clean out the bindings array. Result->NumBindings = NumBindings; auto *Trail = Result->getTrailingObjects<BindingDecl *>(); @@ -3260,16 +3311,17 @@ DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C, return Result; } -void DecompositionDecl::printName(llvm::raw_ostream &os) const { - os << '['; +void DecompositionDecl::printName(llvm::raw_ostream &OS, + const PrintingPolicy &Policy) const { + OS << '['; bool Comma = false; for (const auto *B : bindings()) { if (Comma) - os << ", "; - B->printName(os); + OS << ", "; + B->printName(OS, Policy); Comma = true; } - os << ']'; + OS << ']'; } void MSPropertyDecl::anchor() {} @@ -3305,7 +3357,8 @@ MSGuidDecl *MSGuidDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) MSGuidDecl(nullptr, QualType(), Parts()); } -void MSGuidDecl::printName(llvm::raw_ostream &OS) const { +void MSGuidDecl::printName(llvm::raw_ostream &OS, + const PrintingPolicy &) const { OS << llvm::format("GUID{%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-", PartVal.Part1, PartVal.Part2, PartVal.Part3); unsigned I = 0; @@ -3414,7 +3467,8 @@ UnnamedGlobalConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) { UnnamedGlobalConstantDecl(C, nullptr, QualType(), APValue()); } -void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS) const { +void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS, + const PrintingPolicy &) const { OS << "unnamed-global-constant"; } diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 15c545b59c81..e934a81d086e 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -23,7 +24,6 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/None.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" @@ -403,21 +403,18 @@ ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass( return nullptr; } -void ObjCInterfaceDecl::collectPropertiesToImplement(PropertyMap &PM, - PropertyDeclOrder &PO) const { +void ObjCInterfaceDecl::collectPropertiesToImplement(PropertyMap &PM) const { for (auto *Prop : properties()) { PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; - PO.push_back(Prop); } for (const auto *Ext : known_extensions()) { const ObjCCategoryDecl *ClassExt = Ext; for (auto *Prop : ClassExt->properties()) { PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; - PO.push_back(Prop); } } for (const auto *PI : all_referenced_protocols()) - PI->collectPropertiesToImplement(PM, PO); + PI->collectPropertiesToImplement(PM); // Note, the properties declared only in class extensions are still copied // into the main @interface's property list, and therefore we don't // explicitly, have to search class extension properties. @@ -627,6 +624,17 @@ void ObjCInterfaceDecl::startDefinition() { } } +void ObjCInterfaceDecl::startDuplicateDefinitionForComparison() { + Data.setPointer(nullptr); + allocateDefinitionData(); + // Don't propagate data to other redeclarations. +} + +void ObjCInterfaceDecl::mergeDuplicateDefinitionWithCommon( + const ObjCInterfaceDecl *Definition) { + Data = Definition->Data; +} + ObjCIvarDecl *ObjCInterfaceDecl::lookupInstanceVariable(IdentifierInfo *ID, ObjCInterfaceDecl *&clsDeclared) { // FIXME: Should make sure no callers ever do this. @@ -781,6 +789,33 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( return Method; } +unsigned ObjCInterfaceDecl::getODRHash() { + assert(hasDefinition() && "ODRHash only for records with definitions"); + + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return data().ODRHash; + + // Only calculate hash on first call of getODRHash per record. + ODRHash Hasher; + Hasher.AddObjCInterfaceDecl(getDefinition()); + data().ODRHash = Hasher.CalculateHash(); + setHasODRHash(true); + + return data().ODRHash; +} + +bool ObjCInterfaceDecl::hasODRHash() const { + if (!hasDefinition()) + return false; + return data().HasODRHash; +} + +void ObjCInterfaceDecl::setHasODRHash(bool HasHash) { + assert(hasDefinition() && "Cannot set ODRHash without definition"); + data().HasODRHash = HasHash; +} + //===----------------------------------------------------------------------===// // ObjCMethodDecl //===----------------------------------------------------------------------===// @@ -864,7 +899,7 @@ bool ObjCMethodDecl::isDesignatedInitializerForTheInterface( } bool ObjCMethodDecl::hasParamDestroyedInCallee() const { - for (auto param : parameters()) { + for (auto *param : parameters()) { if (param->isDestroyedInCallee()) return true; } @@ -912,12 +947,12 @@ void ObjCMethodDecl::setMethodParams(ASTContext &C, assert((!SelLocs.empty() || isImplicit()) && "No selector locs for non-implicit method"); if (isImplicit()) - return setParamsAndSelLocs(C, Params, llvm::None); + return setParamsAndSelLocs(C, Params, std::nullopt); setSelLocsKind(hasStandardSelectorLocs(getSelector(), SelLocs, Params, DeclEndLoc)); if (getSelLocsKind() != SelLoc_NonStandard) - return setParamsAndSelLocs(C, Params, llvm::None); + return setParamsAndSelLocs(C, Params, std::nullopt); setParamsAndSelLocs(C, Params, SelLocs); } @@ -1496,7 +1531,7 @@ ObjCTypeParamList *ObjCTypeParamList::create( void ObjCTypeParamList::gatherDefaultTypeArgs( SmallVectorImpl<QualType> &typeArgs) const { typeArgs.reserve(size()); - for (auto typeParam : *this) + for (auto *typeParam : *this) typeArgs.push_back(typeParam->getUnderlyingType()); } @@ -1988,6 +2023,7 @@ void ObjCProtocolDecl::allocateDefinitionData() { assert(!Data.getPointer() && "Protocol already has a definition!"); Data.setPointer(new (getASTContext()) DefinitionData); Data.getPointer()->Definition = this; + Data.getPointer()->HasODRHash = false; } void ObjCProtocolDecl::startDefinition() { @@ -1998,19 +2034,28 @@ void ObjCProtocolDecl::startDefinition() { RD->Data = this->Data; } -void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM, - PropertyDeclOrder &PO) const { +void ObjCProtocolDecl::startDuplicateDefinitionForComparison() { + Data.setPointer(nullptr); + allocateDefinitionData(); + // Don't propagate data to other redeclarations. +} + +void ObjCProtocolDecl::mergeDuplicateDefinitionWithCommon( + const ObjCProtocolDecl *Definition) { + Data = Definition->Data; +} + +void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM) const { if (const ObjCProtocolDecl *PDecl = getDefinition()) { for (auto *Prop : PDecl->properties()) { // Insert into PM if not there already. PM.insert(std::make_pair( std::make_pair(Prop->getIdentifier(), Prop->isClassProperty()), Prop)); - PO.push_back(Prop); } // Scan through protocol's protocols. for (const auto *PI : PDecl->protocols()) - PI->collectPropertiesToImplement(PM, PO); + PI->collectPropertiesToImplement(PM); } } @@ -2042,6 +2087,33 @@ ObjCProtocolDecl::getObjCRuntimeNameAsString() const { return getName(); } +unsigned ObjCProtocolDecl::getODRHash() { + assert(hasDefinition() && "ODRHash only for records with definitions"); + + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return data().ODRHash; + + // Only calculate hash on first call of getODRHash per record. + ODRHash Hasher; + Hasher.AddObjCProtocolDecl(getDefinition()); + data().ODRHash = Hasher.CalculateHash(); + setHasODRHash(true); + + return data().ODRHash; +} + +bool ObjCProtocolDecl::hasODRHash() const { + if (!hasDefinition()) + return false; + return data().HasODRHash; +} + +void ObjCProtocolDecl::setHasODRHash(bool HasHash) { + assert(hasDefinition() && "Cannot set ODRHash without definition"); + data().HasODRHash = HasHash; +} + //===----------------------------------------------------------------------===// // ObjCCategoryDecl //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclOpenMP.cpp b/clang/lib/AST/DeclOpenMP.cpp index 867ef31656f7..e29fc564fd34 100644 --- a/clang/lib/AST/DeclOpenMP.cpp +++ b/clang/lib/AST/DeclOpenMP.cpp @@ -30,7 +30,7 @@ OMPThreadPrivateDecl *OMPThreadPrivateDecl::Create(ASTContext &C, SourceLocation L, ArrayRef<Expr *> VL) { auto *D = OMPDeclarativeDirective::createDirective<OMPThreadPrivateDecl>( - C, DC, llvm::None, VL.size(), L); + C, DC, std::nullopt, VL.size(), L); D->setVars(VL); return D; } diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index b041e2a67e95..f3bad9f45b74 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -72,6 +72,7 @@ namespace { void VisitLabelDecl(LabelDecl *D); void VisitParmVarDecl(ParmVarDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); + void VisitTopLevelStmtDecl(TopLevelStmtDecl *D); void VisitImportDecl(ImportDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitNamespaceDecl(NamespaceDecl *D); @@ -108,6 +109,7 @@ namespace { void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D); void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP); void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP); + void VisitHLSLBufferDecl(HLSLBufferDecl *D); void printTemplateParameters(const TemplateParameterList *Params, bool OmitTemplateKW = false); @@ -462,12 +464,9 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) { Terminator = nullptr; else Terminator = ";"; - } else if (isa<NamespaceDecl>(*D) || isa<LinkageSpecDecl>(*D) || - isa<ObjCImplementationDecl>(*D) || - isa<ObjCInterfaceDecl>(*D) || - isa<ObjCProtocolDecl>(*D) || - isa<ObjCCategoryImplDecl>(*D) || - isa<ObjCCategoryDecl>(*D)) + } else if (isa<NamespaceDecl, LinkageSpecDecl, ObjCImplementationDecl, + ObjCInterfaceDecl, ObjCProtocolDecl, ObjCCategoryImplDecl, + ObjCCategoryDecl, HLSLBufferDecl>(*D)) Terminator = nullptr; else if (isa<EnumConstantDecl>(*D)) { DeclContext::decl_iterator Next = D; @@ -934,6 +933,11 @@ void DeclPrinter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) { Out << ")"; } +void DeclPrinter::VisitTopLevelStmtDecl(TopLevelStmtDecl *D) { + assert(D->getStmt()); + D->getStmt()->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context); +} + void DeclPrinter::VisitImportDecl(ImportDecl *D) { Out << "@import " << D->getImportedModule()->getFullModuleName() << ";\n"; @@ -1658,6 +1662,21 @@ void DeclPrinter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { } } +void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) { + if (D->isCBuffer()) + Out << "cbuffer "; + else + Out << "tbuffer "; + + Out << *D; + + prettyPrintAttributes(D); + + Out << " {\n"; + VisitDeclContext(D); + Indent() << "}"; +} + void DeclPrinter::VisitOMPAllocateDecl(OMPAllocateDecl *D) { Out << "#pragma omp allocate"; if (!D->varlist_empty()) { @@ -1698,7 +1717,7 @@ void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { Out << OpName; } else { assert(D->getDeclName().isIdentifier()); - D->printName(Out); + D->printName(Out, Policy); } Out << " : "; D->getType().print(Out, Policy); @@ -1728,7 +1747,7 @@ void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { void DeclPrinter::VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D) { if (!D->isInvalidDecl()) { Out << "#pragma omp declare mapper ("; - D->printName(Out); + D->printName(Out, Policy); Out << " : "; D->getType().print(Out, Policy); Out << " "; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index e7e5f355809b..531be708b6fd 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -26,7 +26,6 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -36,6 +35,7 @@ #include <cassert> #include <cstdint> #include <memory> +#include <optional> #include <utility> using namespace clang; @@ -131,7 +131,7 @@ unsigned TemplateParameterList::getMinRequiredArguments() const { unsigned NumRequiredArgs = 0; for (const NamedDecl *P : asArray()) { if (P->isTemplateParameterPack()) { - if (Optional<unsigned> Expansions = getExpandedPackSize(P)) { + if (std::optional<unsigned> Expansions = getExpandedPackSize(P)) { NumRequiredArgs += *Expansions; continue; } @@ -250,6 +250,16 @@ bool TemplateDecl::hasAssociatedConstraints() const { return false; } +bool TemplateDecl::isTypeAlias() const { + switch (getKind()) { + case TemplateDecl::TypeAliasTemplate: + case TemplateDecl::BuiltinTemplate: + return true; + default: + return false; + }; +} + //===----------------------------------------------------------------------===// // RedeclarableTemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -343,6 +353,22 @@ void RedeclarableTemplateDecl::addSpecializationImpl( SETraits::getDecl(Entry)); } +ArrayRef<TemplateArgument> RedeclarableTemplateDecl::getInjectedTemplateArgs() { + TemplateParameterList *Params = getTemplateParameters(); + auto *CommonPtr = getCommonPtr(); + if (!CommonPtr->InjectedArgs) { + auto &Context = getASTContext(); + SmallVector<TemplateArgument, 16> TemplateArgs; + Context.getInjectedTemplateArgs(Params, TemplateArgs); + CommonPtr->InjectedArgs = + new (Context) TemplateArgument[TemplateArgs.size()]; + std::copy(TemplateArgs.begin(), TemplateArgs.end(), + CommonPtr->InjectedArgs); + } + + return llvm::ArrayRef(CommonPtr->InjectedArgs, Params->size()); +} + //===----------------------------------------------------------------------===// // FunctionTemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -393,22 +419,6 @@ void FunctionTemplateDecl::addSpecialization( InsertPos); } -ArrayRef<TemplateArgument> FunctionTemplateDecl::getInjectedTemplateArgs() { - TemplateParameterList *Params = getTemplateParameters(); - Common *CommonPtr = getCommonPtr(); - if (!CommonPtr->InjectedArgs) { - auto &Context = getASTContext(); - SmallVector<TemplateArgument, 16> TemplateArgs; - Context.getInjectedTemplateArgs(Params, TemplateArgs); - CommonPtr->InjectedArgs = - new (Context) TemplateArgument[TemplateArgs.size()]; - std::copy(TemplateArgs.begin(), TemplateArgs.end(), - CommonPtr->InjectedArgs); - } - - return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size()); -} - void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { using Base = RedeclarableTemplateDecl; @@ -519,6 +529,9 @@ static void ProfileTemplateParameterList(ASTContext &C, ID.AddInteger(0); ID.AddBoolean(NTTP->isParameterPack()); NTTP->getType().getCanonicalType().Profile(ID); + ID.AddBoolean(NTTP->hasPlaceholderTypeConstraint()); + if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) + E->Profile(ID, C, /*Canonical=*/true); continue; } if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) { @@ -624,13 +637,11 @@ ClassTemplateDecl::getInjectedClassNameSpecialization() { // TemplateTypeParm Allocation/Deallocation Method Implementations //===----------------------------------------------------------------------===// -TemplateTypeParmDecl * -TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC, - SourceLocation KeyLoc, SourceLocation NameLoc, - unsigned D, unsigned P, IdentifierInfo *Id, - bool Typename, bool ParameterPack, - bool HasTypeConstraint, - Optional<unsigned> NumExpanded) { +TemplateTypeParmDecl *TemplateTypeParmDecl::Create( + const ASTContext &C, DeclContext *DC, SourceLocation KeyLoc, + SourceLocation NameLoc, unsigned D, unsigned P, IdentifierInfo *Id, + bool Typename, bool ParameterPack, bool HasTypeConstraint, + std::optional<unsigned> NumExpanded) { auto *TTPDecl = new (C, DC, additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0)) @@ -643,9 +654,9 @@ TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC, TemplateTypeParmDecl * TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID) { - return new (C, ID) TemplateTypeParmDecl(nullptr, SourceLocation(), - SourceLocation(), nullptr, false, - false, None); + return new (C, ID) + TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(), nullptr, + false, false, std::nullopt); } TemplateTypeParmDecl * @@ -653,8 +664,8 @@ TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID, bool HasTypeConstraint) { return new (C, ID, additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0)) - TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(), - nullptr, false, HasTypeConstraint, None); + TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(), nullptr, + false, HasTypeConstraint, std::nullopt); } SourceLocation TemplateTypeParmDecl::getDefaultArgumentLoc() const { @@ -768,12 +779,12 @@ NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, unsigned NumExpandedTypes, bool HasTypeConstraint) { auto *NTTP = - new (C, ID, additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>, - Expr *>( - NumExpandedTypes, HasTypeConstraint ? 1 : 0)) + new (C, ID, + additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>, Expr *>( + NumExpandedTypes, HasTypeConstraint ? 1 : 0)) NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), - 0, 0, nullptr, QualType(), nullptr, None, - None); + 0, 0, nullptr, QualType(), nullptr, + std::nullopt, std::nullopt); NTTP->NumExpandedTypes = NumExpandedTypes; return NTTP; } @@ -841,7 +852,7 @@ TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, auto *TTP = new (C, ID, additionalSizeToAlloc<TemplateParameterList *>(NumExpansions)) TemplateTemplateParmDecl(nullptr, SourceLocation(), 0, 0, nullptr, - nullptr, None); + nullptr, std::nullopt); TTP->NumExpandedParams = NumExpansions; return TTP; } @@ -930,6 +941,14 @@ ClassTemplateSpecializationDecl::Create(ASTContext &Context, TagKind TK, SpecializedTemplate, Args, PrevDecl); Result->setMayHaveOutOfDateDef(false); + // If the template decl is incomplete, copy the external lexical storage from + // the base template. This allows instantiations of incomplete types to + // complete using the external AST if the template's declaration came from an + // external AST. + if (!SpecializedTemplate->getTemplatedDecl()->isCompleteDefinition()) + Result->setHasExternalLexicalStorage( + SpecializedTemplate->getTemplatedDecl()->hasExternalLexicalStorage()); + Context.getTypeDeclType(Result, PrevDecl); return Result; } @@ -1032,6 +1051,44 @@ ConceptDecl *ConceptDecl::CreateDeserialized(ASTContext &C, } //===----------------------------------------------------------------------===// +// ImplicitConceptSpecializationDecl Implementation +//===----------------------------------------------------------------------===// +ImplicitConceptSpecializationDecl::ImplicitConceptSpecializationDecl( + DeclContext *DC, SourceLocation SL, + ArrayRef<TemplateArgument> ConvertedArgs) + : Decl(ImplicitConceptSpecialization, DC, SL), + NumTemplateArgs(ConvertedArgs.size()) { + setTemplateArguments(ConvertedArgs); +} + +ImplicitConceptSpecializationDecl::ImplicitConceptSpecializationDecl( + EmptyShell Empty, unsigned NumTemplateArgs) + : Decl(ImplicitConceptSpecialization, Empty), + NumTemplateArgs(NumTemplateArgs) {} + +ImplicitConceptSpecializationDecl *ImplicitConceptSpecializationDecl::Create( + const ASTContext &C, DeclContext *DC, SourceLocation SL, + ArrayRef<TemplateArgument> ConvertedArgs) { + return new (C, DC, + additionalSizeToAlloc<TemplateArgument>(ConvertedArgs.size())) + ImplicitConceptSpecializationDecl(DC, SL, ConvertedArgs); +} + +ImplicitConceptSpecializationDecl * +ImplicitConceptSpecializationDecl::CreateDeserialized( + const ASTContext &C, unsigned ID, unsigned NumTemplateArgs) { + return new (C, ID, additionalSizeToAlloc<TemplateArgument>(NumTemplateArgs)) + ImplicitConceptSpecializationDecl(EmptyShell{}, NumTemplateArgs); +} + +void ImplicitConceptSpecializationDecl::setTemplateArguments( + ArrayRef<TemplateArgument> Converted) { + assert(Converted.size() == NumTemplateArgs); + std::uninitialized_copy(Converted.begin(), Converted.end(), + getTrailingObjects<TemplateArgument>()); +} + +//===----------------------------------------------------------------------===// // ClassTemplatePartialSpecializationDecl Implementation //===----------------------------------------------------------------------===// void ClassTemplatePartialSpecializationDecl::anchor() {} @@ -1457,8 +1514,8 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) { // template <std::size_t Index, typename ...T> NamedDecl *Params[] = {Index, Ts}; return TemplateParameterList::Create(C, SourceLocation(), SourceLocation(), - llvm::makeArrayRef(Params), - SourceLocation(), nullptr); + llvm::ArrayRef(Params), SourceLocation(), + nullptr); } static TemplateParameterList *createBuiltinTemplateParameterList( @@ -1511,9 +1568,10 @@ TemplateParamObjectDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return TPOD; } -void TemplateParamObjectDecl::printName(llvm::raw_ostream &OS) const { +void TemplateParamObjectDecl::printName(llvm::raw_ostream &OS, + const PrintingPolicy &Policy) const { OS << "<template param "; - printAsExpr(OS); + printAsExpr(OS, Policy); OS << ">"; } @@ -1535,3 +1593,56 @@ void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const { getValue().printPretty(OS, Policy, getType(), &getASTContext()); } + +TemplateParameterList *clang::getReplacedTemplateParameterList(Decl *D) { + switch (D->getKind()) { + case Decl::Kind::ClassTemplate: + return cast<ClassTemplateDecl>(D)->getTemplateParameters(); + case Decl::Kind::ClassTemplateSpecialization: { + const auto *CTSD = cast<ClassTemplateSpecializationDecl>(D); + auto P = CTSD->getSpecializedTemplateOrPartial(); + if (const auto *CTPSD = + P.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) + return CTPSD->getTemplateParameters(); + return cast<ClassTemplateDecl *>(P)->getTemplateParameters(); + } + case Decl::Kind::ClassTemplatePartialSpecialization: + return cast<ClassTemplatePartialSpecializationDecl>(D) + ->getTemplateParameters(); + case Decl::Kind::TypeAliasTemplate: + return cast<TypeAliasTemplateDecl>(D)->getTemplateParameters(); + case Decl::Kind::BuiltinTemplate: + return cast<BuiltinTemplateDecl>(D)->getTemplateParameters(); + case Decl::Kind::CXXDeductionGuide: + case Decl::Kind::CXXConversion: + case Decl::Kind::CXXConstructor: + case Decl::Kind::CXXDestructor: + case Decl::Kind::CXXMethod: + case Decl::Kind::Function: + return cast<FunctionDecl>(D) + ->getTemplateSpecializationInfo() + ->getTemplate() + ->getTemplateParameters(); + case Decl::Kind::FunctionTemplate: + return cast<FunctionTemplateDecl>(D)->getTemplateParameters(); + case Decl::Kind::VarTemplate: + return cast<VarTemplateDecl>(D)->getTemplateParameters(); + case Decl::Kind::VarTemplateSpecialization: { + const auto *VTSD = cast<VarTemplateSpecializationDecl>(D); + auto P = VTSD->getSpecializedTemplateOrPartial(); + if (const auto *VTPSD = + P.dyn_cast<VarTemplatePartialSpecializationDecl *>()) + return VTPSD->getTemplateParameters(); + return cast<VarTemplateDecl *>(P)->getTemplateParameters(); + } + case Decl::Kind::VarTemplatePartialSpecialization: + return cast<VarTemplatePartialSpecializationDecl>(D) + ->getTemplateParameters(); + case Decl::Kind::TemplateTemplateParm: + return cast<TemplateTemplateParmDecl>(D)->getTemplateParameters(); + case Decl::Kind::Concept: + return cast<ConceptDecl>(D)->getTemplateParameters(); + default: + llvm_unreachable("Unhandled templated declaration kind"); + } +} diff --git a/clang/lib/AST/DeclarationName.cpp b/clang/lib/AST/DeclarationName.cpp index b2232ddfced3..c1219041a466 100644 --- a/clang/lib/AST/DeclarationName.cpp +++ b/clang/lib/AST/DeclarationName.cpp @@ -72,15 +72,9 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { } unsigned LN = LHSSelector.getNumArgs(), RN = RHSSelector.getNumArgs(); for (unsigned I = 0, N = std::min(LN, RN); I != N; ++I) { - switch (LHSSelector.getNameForSlot(I).compare( - RHSSelector.getNameForSlot(I))) { - case -1: - return -1; - case 1: - return 1; - default: - break; - } + if (int Compare = LHSSelector.getNameForSlot(I).compare( + RHSSelector.getNameForSlot(I))) + return Compare; } return compareInt(LN, RN); diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index ca477e6500c5..67862a8692ac 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -37,6 +37,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstring> +#include <optional> using namespace clang; const Expr *Expr::getBestDynamicClassTypeExpr() const { @@ -203,6 +204,88 @@ bool Expr::isKnownToHaveBooleanValue(bool Semantic) const { return false; } +bool Expr::isFlexibleArrayMemberLike( + ASTContext &Context, + LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel, + bool IgnoreTemplateOrMacroSubstitution) const { + + // For compatibility with existing code, we treat arrays of length 0 or + // 1 as flexible array members. + const auto *CAT = Context.getAsConstantArrayType(getType()); + if (CAT) { + llvm::APInt Size = CAT->getSize(); + + using FAMKind = LangOptions::StrictFlexArraysLevelKind; + + if (StrictFlexArraysLevel == FAMKind::IncompleteOnly) + return false; + + // GCC extension, only allowed to represent a FAM. + if (Size == 0) + return true; + + if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1)) + return false; + + if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2)) + return false; + } else if (!Context.getAsIncompleteArrayType(getType())) + return false; + + const Expr *E = IgnoreParens(); + + const NamedDecl *ND = nullptr; + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) + ND = DRE->getDecl(); + else if (const auto *ME = dyn_cast<MemberExpr>(E)) + ND = ME->getMemberDecl(); + else if (const auto *IRE = dyn_cast<ObjCIvarRefExpr>(E)) + return IRE->getDecl()->getNextIvar() == nullptr; + + if (!ND) + return false; + + // A flexible array member must be the last member in the class. + // FIXME: If the base type of the member expr is not FD->getParent(), + // this should not be treated as a flexible array member access. + if (const auto *FD = dyn_cast<FieldDecl>(ND)) { + // GCC treats an array memeber of a union as an FAM if the size is one or + // zero. + if (CAT) { + llvm::APInt Size = CAT->getSize(); + if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne())) + return true; + } + + // Don't consider sizes resulting from macro expansions or template argument + // substitution to form C89 tail-padded arrays. + if (IgnoreTemplateOrMacroSubstitution) { + TypeSourceInfo *TInfo = FD->getTypeSourceInfo(); + while (TInfo) { + TypeLoc TL = TInfo->getTypeLoc(); + // Look through typedefs. + if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) { + const TypedefNameDecl *TDL = TTL.getTypedefNameDecl(); + TInfo = TDL->getTypeSourceInfo(); + continue; + } + if (ConstantArrayTypeLoc CTL = TL.getAs<ConstantArrayTypeLoc>()) { + const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr()); + if (!SizeExpr || SizeExpr->getExprLoc().isMacroID()) + return false; + } + break; + } + } + + RecordDecl::field_iterator FI( + DeclContext::decl_iterator(const_cast<FieldDecl *>(FD))); + return ++FI == FD->getParent()->field_end(); + } + + return false; +} + const ValueDecl * Expr::getAsBuiltinConstantDeclRef(const ASTContext &Context) const { Expr::EvalResult Eval; @@ -277,7 +360,7 @@ ConstantExpr::getStorageKind(const APValue &Value) { case APValue::Int: if (!Value.getInt().needsCleanup()) return ConstantExpr::RSK_Int64; - LLVM_FALLTHROUGH; + [[fallthrough]]; default: return ConstantExpr::RSK_APValue; } @@ -562,10 +645,10 @@ std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context) const { std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context, QualType Ty) { auto MangleCallback = [](ASTContext &Ctx, - const NamedDecl *ND) -> llvm::Optional<unsigned> { + const NamedDecl *ND) -> std::optional<unsigned> { if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) return RD->getDeviceLambdaManglingNumber(); - return llvm::None; + return std::nullopt; }; std::unique_ptr<MangleContext> Ctx{ItaniumMangleContext::create( @@ -2633,7 +2716,7 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, } // Fallthrough for generic call handling. - LLVM_FALLTHROUGH; + [[fallthrough]]; } case CallExprClass: case CXXMemberCallExprClass: @@ -3562,6 +3645,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ShuffleVectorExprClass: case ConvertVectorExprClass: case AsTypeExprClass: + case CXXParenListInitExprClass: // These have a side-effect if any subexpression does. break; @@ -3609,7 +3693,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, DCE->getCastKind() == CK_Dynamic) return true; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case ImplicitCastExprClass: case CStyleCastExprClass: case CXXStaticCastExprClass: @@ -3857,7 +3941,7 @@ Expr::isNullPointerConstant(ASTContext &Ctx, if (getType().isNull()) return NPCK_NotNull; - // C++11 nullptr_t is always a null pointer constant. + // C++11/C2x nullptr_t is always a null pointer constant. if (getType()->isNullPtrType()) return NPCK_CXX11_nullptr; @@ -4451,7 +4535,8 @@ DesignatedInitUpdateExpr::DesignatedInitUpdateExpr(const ASTContext &C, OK_Ordinary) { BaseAndUpdaterExprs[0] = baseExpr; - InitListExpr *ILE = new (C) InitListExpr(C, lBraceLoc, None, rBraceLoc); + InitListExpr *ILE = + new (C) InitListExpr(C, lBraceLoc, std::nullopt, rBraceLoc); ILE->setType(baseExpr->getType()); BaseAndUpdaterExprs[1] = ILE; @@ -4831,7 +4916,7 @@ RecoveryExpr::RecoveryExpr(ASTContext &Ctx, QualType T, SourceLocation BeginLoc, OK_Ordinary), BeginLoc(BeginLoc), EndLoc(EndLoc), NumExprs(SubExprs.size()) { assert(!T.isNull()); - assert(llvm::all_of(SubExprs, [](Expr* E) { return E != nullptr; })); + assert(!llvm::is_contained(SubExprs, nullptr)); llvm::copy(SubExprs, getTrailingObjects<Expr *>()); setDependence(computeDependence(this)); diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 891105692980..2a9e33595013 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -38,6 +38,7 @@ #include <cstddef> #include <cstring> #include <memory> +#include <optional> using namespace clang; @@ -182,7 +183,7 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens, - Optional<Expr *> ArraySize, + std::optional<Expr *> ArraySize, InitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, @@ -244,7 +245,7 @@ CXXNewExpr::Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef<Expr *> PlacementArgs, SourceRange TypeIdParens, - Optional<Expr *> ArraySize, + std::optional<Expr *> ArraySize, InitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange) { @@ -314,7 +315,7 @@ QualType CXXDeleteExpr::getDestroyedType() const { // CXXPseudoDestructorExpr PseudoDestructorTypeStorage::PseudoDestructorTypeStorage(TypeSourceInfo *Info) : Type(Info) { - Location = Info->getTypeLoc().getLocalSourceRange().getBegin(); + Location = Info->getTypeLoc().getBeginLoc(); } CXXPseudoDestructorExpr::CXXPseudoDestructorExpr( @@ -341,7 +342,7 @@ QualType CXXPseudoDestructorExpr::getDestroyedType() const { SourceLocation CXXPseudoDestructorExpr::getEndLoc() const { SourceLocation End = DestroyedType.getLocation(); if (TypeSourceInfo *TInfo = DestroyedType.getTypeSourceInfo()) - End = TInfo->getTypeLoc().getLocalSourceRange().getEnd(); + End = TInfo->getTypeLoc().getSourceRange().getEnd(); return End; } @@ -949,9 +950,43 @@ const IdentifierInfo *UserDefinedLiteral::getUDSuffix() const { return cast<FunctionDecl>(getCalleeDecl())->getLiteralIdentifier(); } +CXXDefaultArgExpr *CXXDefaultArgExpr::CreateEmpty(const ASTContext &C, + bool HasRewrittenInit) { + size_t Size = totalSizeToAlloc<Expr *>(HasRewrittenInit); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(EmptyShell(), HasRewrittenInit); +} + +CXXDefaultArgExpr *CXXDefaultArgExpr::Create(const ASTContext &C, + SourceLocation Loc, + ParmVarDecl *Param, + Expr *RewrittenExpr, + DeclContext *UsedContext) { + size_t Size = totalSizeToAlloc<Expr *>(RewrittenExpr != nullptr); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, + RewrittenExpr, UsedContext); +} + +Expr *CXXDefaultArgExpr::getExpr() { + return CXXDefaultArgExprBits.HasRewrittenInit ? getAdjustedRewrittenExpr() + : getParam()->getDefaultArg(); +} + +Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() { + assert(hasRewrittenInit() && + "expected this CXXDefaultArgExpr to have a rewritten init."); + Expr *Init = getRewrittenExpr(); + if (auto *E = dyn_cast_if_present<FullExpr>(Init)) + if (!isa<ConstantExpr>(E)) + return E->getSubExpr(); + return Init; +} + CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc, FieldDecl *Field, - QualType Ty, DeclContext *UsedContext) + QualType Ty, DeclContext *UsedContext, + Expr *RewrittenInitExpr) : Expr(CXXDefaultInitExprClass, Ty.getNonLValueExprType(Ctx), Ty->isLValueReferenceType() ? VK_LValue : Ty->isRValueReferenceType() ? VK_XValue @@ -959,11 +994,43 @@ CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx, /*FIXME*/ OK_Ordinary), Field(Field), UsedContext(UsedContext) { CXXDefaultInitExprBits.Loc = Loc; + CXXDefaultInitExprBits.HasRewrittenInit = RewrittenInitExpr != nullptr; + + if (CXXDefaultInitExprBits.HasRewrittenInit) + *getTrailingObjects<Expr *>() = RewrittenInitExpr; + assert(Field->hasInClassInitializer()); setDependence(computeDependence(this)); } +CXXDefaultInitExpr *CXXDefaultInitExpr::CreateEmpty(const ASTContext &C, + bool HasRewrittenInit) { + size_t Size = totalSizeToAlloc<Expr *>(HasRewrittenInit); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultInitExpr)); + return new (Mem) CXXDefaultInitExpr(EmptyShell(), HasRewrittenInit); +} + +CXXDefaultInitExpr *CXXDefaultInitExpr::Create(const ASTContext &Ctx, + SourceLocation Loc, + FieldDecl *Field, + DeclContext *UsedContext, + Expr *RewrittenInitExpr) { + + size_t Size = totalSizeToAlloc<Expr *>(RewrittenInitExpr != nullptr); + auto *Mem = Ctx.Allocate(Size, alignof(CXXDefaultInitExpr)); + return new (Mem) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), + UsedContext, RewrittenInitExpr); +} + +Expr *CXXDefaultInitExpr::getExpr() { + assert(Field->getInClassInitializer() && "initializer hasn't been parsed"); + if (hasRewrittenInit()) + return getRewrittenExpr(); + + return Field->getInClassInitializer(); +} + CXXTemporary *CXXTemporary::Create(const ASTContext &C, const CXXDestructorDecl *Destructor) { return new (C) CXXTemporary(Destructor); @@ -1087,7 +1154,7 @@ CXXConstructExpr::CXXConstructExpr(StmtClass SC, EmptyShell Empty, : Expr(SC, Empty), NumArgs(NumArgs) {} LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit, - LambdaCaptureKind Kind, VarDecl *Var, + LambdaCaptureKind Kind, ValueDecl *Var, SourceLocation EllipsisLoc) : DeclAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc) { unsigned Bits = 0; @@ -1097,7 +1164,7 @@ LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit, switch (Kind) { case LCK_StarThis: Bits |= Capture_ByCopy; - LLVM_FALLTHROUGH; + [[fallthrough]]; case LCK_This: assert(!Var && "'this' capture cannot have a variable!"); Bits |= Capture_This; @@ -1105,7 +1172,7 @@ LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit, case LCK_ByCopy: Bits |= Capture_ByCopy; - LLVM_FALLTHROUGH; + [[fallthrough]]; case LCK_ByRef: assert(Var && "capture must have a variable!"); break; @@ -1211,16 +1278,16 @@ const CompoundStmt *LambdaExpr::getCompoundStmtBody() const { } bool LambdaExpr::isInitCapture(const LambdaCapture *C) const { - return (C->capturesVariable() && C->getCapturedVar()->isInitCapture() && - (getCallOperator() == C->getCapturedVar()->getDeclContext())); + return C->capturesVariable() && C->getCapturedVar()->isInitCapture() && + getCallOperator() == C->getCapturedVar()->getDeclContext(); } LambdaExpr::capture_iterator LambdaExpr::capture_begin() const { - return getLambdaClass()->getLambdaData().Captures; + return getLambdaClass()->captures_begin(); } LambdaExpr::capture_iterator LambdaExpr::capture_end() const { - return capture_begin() + capture_size(); + return getLambdaClass()->captures_end(); } LambdaExpr::capture_range LambdaExpr::captures() const { @@ -1232,9 +1299,8 @@ LambdaExpr::capture_iterator LambdaExpr::explicit_capture_begin() const { } LambdaExpr::capture_iterator LambdaExpr::explicit_capture_end() const { - struct CXXRecordDecl::LambdaDefinitionData &Data - = getLambdaClass()->getLambdaData(); - return Data.Captures + Data.NumExplicitCaptures; + return capture_begin() + + getLambdaClass()->getLambdaData().NumExplicitCaptures; } LambdaExpr::capture_range LambdaExpr::explicit_captures() const { @@ -1555,12 +1621,12 @@ CXXRecordDecl *UnresolvedMemberExpr::getNamingClass() { return Record; } -SizeOfPackExpr * -SizeOfPackExpr::Create(ASTContext &Context, SourceLocation OperatorLoc, - NamedDecl *Pack, SourceLocation PackLoc, - SourceLocation RParenLoc, - Optional<unsigned> Length, - ArrayRef<TemplateArgument> PartialArgs) { +SizeOfPackExpr *SizeOfPackExpr::Create(ASTContext &Context, + SourceLocation OperatorLoc, + NamedDecl *Pack, SourceLocation PackLoc, + SourceLocation RParenLoc, + std::optional<unsigned> Length, + ArrayRef<TemplateArgument> PartialArgs) { void *Storage = Context.Allocate(totalSizeToAlloc<TemplateArgument>(PartialArgs.size())); return new (Storage) SizeOfPackExpr(Context.getSizeType(), OperatorLoc, Pack, @@ -1574,6 +1640,11 @@ SizeOfPackExpr *SizeOfPackExpr::CreateDeserialized(ASTContext &Context, return new (Storage) SizeOfPackExpr(EmptyShell(), NumPartialArgs); } +NonTypeTemplateParmDecl *SubstNonTypeTemplateParmExpr::getParameter() const { + return cast<NonTypeTemplateParmDecl>( + getReplacedTemplateParameterList(getAssociatedDecl())->asArray()[Index]); +} + QualType SubstNonTypeTemplateParmExpr::getParameterType( const ASTContext &Context) const { // Note that, for a class type NTTP, we will have an lvalue of type 'const @@ -1584,17 +1655,24 @@ QualType SubstNonTypeTemplateParmExpr::getParameterType( } SubstNonTypeTemplateParmPackExpr::SubstNonTypeTemplateParmPackExpr( - QualType T, ExprValueKind ValueKind, NonTypeTemplateParmDecl *Param, - SourceLocation NameLoc, const TemplateArgument &ArgPack) + QualType T, ExprValueKind ValueKind, SourceLocation NameLoc, + const TemplateArgument &ArgPack, Decl *AssociatedDecl, unsigned Index) : Expr(SubstNonTypeTemplateParmPackExprClass, T, ValueKind, OK_Ordinary), - Param(Param), Arguments(ArgPack.pack_begin()), - NumArguments(ArgPack.pack_size()), NameLoc(NameLoc) { + AssociatedDecl(AssociatedDecl), Arguments(ArgPack.pack_begin()), + NumArguments(ArgPack.pack_size()), Index(Index), NameLoc(NameLoc) { + assert(AssociatedDecl != nullptr); setDependence(ExprDependence::TypeValueInstantiation | ExprDependence::UnexpandedPack); } +NonTypeTemplateParmDecl * +SubstNonTypeTemplateParmPackExpr::getParameterPack() const { + return cast<NonTypeTemplateParmDecl>( + getReplacedTemplateParameterList(getAssociatedDecl())->asArray()[Index]); +} + TemplateArgument SubstNonTypeTemplateParmPackExpr::getArgumentPack() const { - return TemplateArgument(llvm::makeArrayRef(Arguments, NumArguments)); + return TemplateArgument(llvm::ArrayRef(Arguments, NumArguments)); } FunctionParmPackExpr::FunctionParmPackExpr(QualType T, VarDecl *ParamPack, @@ -1746,3 +1824,21 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, HasFPFeatures, Empty); } + +CXXParenListInitExpr * +CXXParenListInitExpr::Create(ASTContext &C, ArrayRef<Expr *> Args, QualType T, + unsigned NumUserSpecifiedExprs, + SourceLocation InitLoc, SourceLocation LParenLoc, + SourceLocation RParenLoc) { + void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(Args.size())); + return new (Mem) CXXParenListInitExpr(Args, T, NumUserSpecifiedExprs, InitLoc, + LParenLoc, RParenLoc); +} + +CXXParenListInitExpr *CXXParenListInitExpr::CreateEmpty(ASTContext &C, + unsigned NumExprs, + EmptyShell Empty) { + void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumExprs), + alignof(CXXParenListInitExpr)); + return new (Mem) CXXParenListInitExpr(Empty, NumExprs); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 6c122cac2c60..12193b7812f9 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -160,7 +160,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXPseudoDestructorExprClass: case Expr::UnaryExprOrTypeTraitExprClass: case Expr::CXXNewExprClass: - case Expr::CXXThisExprClass: case Expr::CXXNullPtrLiteralExprClass: case Expr::ImaginaryLiteralClass: case Expr::GNUNullExprClass: @@ -205,6 +204,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::RequiresExprClass: return Cl::CL_PRValue; + // Make HLSL this reference-like + case Expr::CXXThisExprClass: + return Lang.HLSL ? Cl::CL_LValue : Cl::CL_PRValue; + case Expr::ConstantExprClass: return ClassifyInternal(Ctx, cast<ConstantExpr>(E)->getSubExpr()); @@ -442,6 +445,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SYCLUniqueStableNameExprClass: return Cl::CL_PRValue; break; + + case Expr::CXXParenListInitExprClass: + if (isa<ArrayType>(E->getType())) + return Cl::CL_ArrayTemporary; + return Cl::CL_ClassTemporary; } llvm_unreachable("unhandled expression kind in classification"); diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp index c17453fb45fb..fc8f1eb2abf1 100644 --- a/clang/lib/AST/ExprConcepts.cpp +++ b/clang/lib/AST/ExprConcepts.cpp @@ -35,16 +35,15 @@ ConceptSpecializationExpr::ConceptSpecializationExpr( SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, + ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_PRValue, OK_Ordinary), ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, ArgsAsWritten), - NumTemplateArgs(ConvertedArgs.size()), + SpecDecl(SpecDecl), Satisfaction(Satisfaction ? ASTConstraintSatisfaction::Create(C, *Satisfaction) : nullptr) { - setTemplateArguments(ConvertedArgs); setDependence(computeDependence(this, /*ValueDependent=*/!Satisfaction)); // Currently guaranteed by the fact concepts can only be at namespace-scope. @@ -56,50 +55,34 @@ ConceptSpecializationExpr::ConceptSpecializationExpr( "should not be value-dependent"); } -ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, - unsigned NumTemplateArgs) - : Expr(ConceptSpecializationExprClass, Empty), - NumTemplateArgs(NumTemplateArgs) {} +ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty) + : Expr(ConceptSpecializationExprClass, Empty) {} -void ConceptSpecializationExpr::setTemplateArguments( - ArrayRef<TemplateArgument> Converted) { - assert(Converted.size() == NumTemplateArgs); - std::uninitialized_copy(Converted.begin(), Converted.end(), - getTrailingObjects<TemplateArgument>()); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(const ASTContext &C, - NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - ConvertedArgs.size())); - return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, - ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten, - ConvertedArgs, Satisfaction); +ConceptSpecializationExpr *ConceptSpecializationExpr::Create( + const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ImplicitConceptSpecializationDecl *SpecDecl, + const ConstraintSatisfaction *Satisfaction) { + return new (C) ConceptSpecializationExpr( + C, NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, + ArgsAsWritten, SpecDecl, Satisfaction); } ConceptSpecializationExpr::ConceptSpecializationExpr( const ASTContext &C, ConceptDecl *NamedConcept, - ArrayRef<TemplateArgument> ConvertedArgs, + ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_PRValue, OK_Ordinary), ConceptReference(NestedNameSpecifierLoc(), SourceLocation(), DeclarationNameInfo(), NamedConcept, NamedConcept, nullptr), - NumTemplateArgs(ConvertedArgs.size()), + SpecDecl(SpecDecl), Satisfaction(Satisfaction ? ASTConstraintSatisfaction::Create(C, *Satisfaction) : nullptr) { - setTemplateArguments(ConvertedArgs); ExprDependence D = ExprDependence::None; if (!Satisfaction) D |= ExprDependence::Value; @@ -110,26 +93,14 @@ ConceptSpecializationExpr::ConceptSpecializationExpr( setDependence(D); } -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(const ASTContext &C, - ConceptDecl *NamedConcept, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction, - bool Dependent, - bool ContainsUnexpandedParameterPack) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - ConvertedArgs.size())); - return new (Buffer) ConceptSpecializationExpr( - C, NamedConcept, ConvertedArgs, Satisfaction, Dependent, - ContainsUnexpandedParameterPack); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, - unsigned NumTemplateArgs) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - NumTemplateArgs)); - return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +ConceptSpecializationExpr *ConceptSpecializationExpr::Create( + const ASTContext &C, ConceptDecl *NamedConcept, + ImplicitConceptSpecializationDecl *SpecDecl, + const ConstraintSatisfaction *Satisfaction, bool Dependent, + bool ContainsUnexpandedParameterPack) { + return new (C) + ConceptSpecializationExpr(C, NamedConcept, SpecDecl, Satisfaction, + Dependent, ContainsUnexpandedParameterPack); } const TypeConstraint * diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9d92c848ccb8..912a210fd254 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -52,13 +52,14 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" #include <cstring> #include <functional> +#include <optional> #define DEBUG_TYPE "exprconstant" @@ -68,7 +69,6 @@ using llvm::APInt; using llvm::APSInt; using llvm::APFloat; using llvm::FixedPointSemantics; -using llvm::Optional; namespace { struct LValue; @@ -578,7 +578,7 @@ namespace { /// LambdaCaptureFields - Mapping from captured variables/this to /// corresponding data members in the closure class. - llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields; FieldDecl *LambdaThisCaptureField; CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, @@ -592,11 +592,6 @@ namespace { auto LB = Temporaries.lower_bound(KV); if (LB != Temporaries.end() && LB->first == KV) return &LB->second; - // Pair (Key,Version) wasn't found in the map. Check that no elements - // in the map have 'Key' as their key. - assert((LB == Temporaries.end() || LB->first.first != Key) && - (LB == Temporaries.begin() || std::prev(LB)->first.first != Key) && - "Element with key 'Key' found in map"); return nullptr; } @@ -660,6 +655,19 @@ namespace { CallStackFrame &Frame; const LValue *OldThis; }; + + // A shorthand time trace scope struct, prints source range, for example + // {"name":"EvaluateAsRValue","args":{"detail":"<test.cc:8:21, col:25>"}}} + class ExprTimeTraceScope { + public: + ExprTimeTraceScope(const Expr *E, const ASTContext &Ctx, StringRef Name) + : TimeScope(Name, [E, &Ctx] { + return E->getSourceRange().printToString(Ctx.getSourceManager()); + }) {} + + private: + llvm::TimeTraceScope TimeScope; + }; } static bool HandleDestruction(EvalInfo &Info, const Expr *E, @@ -917,10 +925,6 @@ namespace { /// fold (not just why it's not strictly a constant expression)? bool HasFoldFailureDiagnostic; - /// Whether or not we're in a context where the front end requires a - /// constant value. - bool InConstantContext; - /// Whether we're checking that an expression is a potential constant /// expression. If so, do not fail on constructs that could become constant /// later on (such as a use of an undefined global). @@ -976,8 +980,7 @@ namespace { BottomFrame(*this, SourceLocation(), nullptr, nullptr, CallRef()), EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), InConstantContext(false), - EvalMode(Mode) {} + HasFoldFailureDiagnostic(false), EvalMode(Mode) {} ~EvalInfo() { discardCleanups(); @@ -1036,8 +1039,8 @@ namespace { APValue *createHeapAlloc(const Expr *E, QualType T, LValue &LV); - Optional<DynAlloc*> lookupDynamicAlloc(DynamicAllocLValue DA) { - Optional<DynAlloc*> Result; + std::optional<DynAlloc *> lookupDynamicAlloc(DynamicAllocLValue DA) { + std::optional<DynAlloc *> Result; auto It = HeapAllocs.find(DA); if (It != HeapAllocs.end()) Result = &It->second; @@ -1132,7 +1135,7 @@ namespace { if (!HasFoldFailureDiagnostic) break; // We've already failed to fold something. Keep that diagnostic. - LLVM_FALLTHROUGH; + [[fallthrough]]; case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: setActiveDiagnostic(false); @@ -1219,7 +1222,7 @@ namespace { /// (Foo(), 1) // use noteSideEffect /// (Foo() || true) // use noteSideEffect /// Foo() + 1 // use noteFailure - LLVM_NODISCARD bool noteFailure() { + [[nodiscard]] bool noteFailure() { // Failure when evaluating some expression often means there is some // subexpression whose evaluation was skipped. Therefore, (because we // don't track whether we skipped an expression when unwinding after an @@ -1954,8 +1957,8 @@ static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { return true; } -/// Should this call expression be treated as a constant? -static bool IsConstantCall(const CallExpr *E) { +/// Should this call expression be treated as a no-op? +static bool IsNoOpCall(const CallExpr *E) { unsigned Builtin = E->getBuiltinCallee(); return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || Builtin == Builtin::BI__builtin___NSStringMakeConstantString || @@ -2006,7 +2009,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::ObjCBoxedExprClass: return cast<ObjCBoxedExpr>(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: - return IsConstantCall(cast<CallExpr>(E)); + return IsNoOpCall(cast<CallExpr>(E)); // For GCC compatibility, &&label has static storage duration. case Expr::AddrLabelExprClass: return true; @@ -2095,7 +2098,7 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here); else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) { // FIXME: Produce a note for dangling pointers too. - if (Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA)) + if (std::optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA)) Info.Note((*Alloc)->AllocExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here); } @@ -2174,12 +2177,10 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // assumed to be global here. if (!IsGlobalLValue(Base)) { if (Info.getLangOpts().CPlusPlus11) { - const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>(); Info.FFDiag(Loc, diag::note_constexpr_non_global, 1) - << IsReferenceType << !Designator.Entries.empty() - << !!VD << VD; - - auto *VarD = dyn_cast_or_null<VarDecl>(VD); + << IsReferenceType << !Designator.Entries.empty() << !!BaseVD + << BaseVD; + auto *VarD = dyn_cast_or_null<VarDecl>(BaseVD); if (VarD && VarD->isConstexpr()) { // Non-static local constexpr variables have unintuitive semantics: // constexpr int a = 1; @@ -2470,6 +2471,7 @@ static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. if (!Value.getLValueBase()) { + // TODO: Should a non-null pointer with an offset of zero evaluate to true? Result = !Value.getLValueOffset().isZero(); return true; } @@ -2482,6 +2484,7 @@ static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { } static bool HandleConversionToBool(const APValue &Val, bool &Result) { + // TODO: This function should produce notes if it fails. switch (Val.getKind()) { case APValue::None: case APValue::Indeterminate: @@ -2506,6 +2509,9 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { case APValue::LValue: return EvalPointerValueAsBool(Val, Result); case APValue::MemberPointer: + if (Val.getMemberPointerDecl() && Val.getMemberPointerDecl()->isWeak()) { + return false; + } Result = Val.getMemberPointerDecl(); return true; case APValue::Vector: @@ -2636,14 +2642,9 @@ static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, QualType SrcType, const APSInt &Value, QualType DestType, APFloat &Result) { Result = APFloat(Info.Ctx.getFloatTypeSemantics(DestType), 1); - APFloat::opStatus St = Result.convertFromAPInt(Value, Value.isSigned(), - APFloat::rmNearestTiesToEven); - if (!Info.InConstantContext && St != llvm::APFloatBase::opOK && - FPO.isFPConstrained()) { - Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); - return false; - } - return true; + llvm::RoundingMode RM = getActiveRoundingMode(Info, E); + APFloat::opStatus St = Result.convertFromAPInt(Value, Value.isSigned(), RM); + return checkFloatingPointResult(Info, E, St); } static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E, @@ -2743,6 +2744,7 @@ static bool CheckedIntArithmetic(EvalInfo &Info, const Expr *E, static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS, BinaryOperatorKind Opcode, APSInt RHS, APSInt &Result) { + bool HandleOverflowResult = true; switch (Opcode) { default: Info.FFDiag(E); @@ -2765,14 +2767,14 @@ static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS, Info.FFDiag(E, diag::note_expr_divide_by_zero); return false; } - Result = (Opcode == BO_Rem ? LHS % RHS : LHS / RHS); // Check for overflow case: INT_MIN / -1 or INT_MIN % -1. APSInt supports // this operation and gives the two's complement result. if (RHS.isNegative() && RHS.isAllOnes() && LHS.isSigned() && LHS.isMinSignedValue()) - return HandleOverflow(Info, E, -LHS.extend(LHS.getBitWidth() + 1), - E->getType()); - return true; + HandleOverflowResult = HandleOverflow( + Info, E, -LHS.extend(LHS.getBitWidth() + 1), E->getType()); + Result = (Opcode == BO_Rem ? LHS % RHS : LHS / RHS); + return HandleOverflowResult; case BO_Shl: { if (Info.getLangOpts().OpenCL) // OpenCL 6.3j: shift values are effectively % word size of LHS. @@ -3647,9 +3649,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) && ObjType->isRecordType() && Info.isEvaluatingCtorDtor( - Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(), - Sub.Entries.begin() + I)) != - ConstructionPhase::None) { + Obj.Base, + llvm::ArrayRef(Sub.Entries.begin(), Sub.Entries.begin() + I)) != + ConstructionPhase::None) { ObjType = Info.Ctx.getCanonicalType(ObjType); ObjType.removeLocalConst(); ObjType.removeLocalVolatile(); @@ -4129,7 +4131,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!evaluateVarDeclInit(Info, E, VD, Frame, LVal.getLValueVersion(), BaseVal)) return CompleteObject(); } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) { - Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA); + std::optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA); if (!Alloc) { Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK; return CompleteObject(); @@ -4844,6 +4846,8 @@ enum EvalStmtResult { } static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) { + if (VD->isInvalidDecl()) + return false; // We don't need to evaluate the initializer for a static local. if (!VD->hasLocalStorage()) return true; @@ -5040,8 +5044,10 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, static bool CheckLocalVariableDeclaration(EvalInfo &Info, const VarDecl *VD) { // An expression E is a core constant expression unless the evaluation of E // would evaluate one of the following: [C++2b] - a control flow that passes - // through a declaration of a variable with static or thread storage duration. - if (VD->isLocalVarDecl() && VD->isStaticLocal()) { + // through a declaration of a variable with static or thread storage duration + // unless that variable is usable in constant expressions. + if (VD->isLocalVarDecl() && VD->isStaticLocal() && + !VD->isUsableInConstantExpressions(Info.Ctx)) { Info.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local) << (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD; return false; @@ -5663,13 +5669,15 @@ static const CXXRecordDecl *getBaseClassType(SubobjectDesignator &Designator, } /// Determine the dynamic type of an object. -static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E, - LValue &This, AccessKinds AK) { +static std::optional<DynamicType> ComputeDynamicType(EvalInfo &Info, + const Expr *E, + LValue &This, + AccessKinds AK) { // If we don't have an lvalue denoting an object of class type, there is no // meaningful dynamic type. (We consider objects of non-class type to have no // dynamic type.) if (!checkDynamicType(Info, E, This, AK, true)) - return None; + return std::nullopt; // Refuse to compute a dynamic type in the presence of virtual bases. This // shouldn't happen other than in constant-folding situations, since literal @@ -5681,7 +5689,7 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E, This.Designator.MostDerivedType->getAsCXXRecordDecl(); if (!Class || Class->getNumVBases()) { Info.FFDiag(E); - return None; + return std::nullopt; } // FIXME: For very deep class hierarchies, it might be beneficial to use a @@ -5714,14 +5722,14 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E, // 'This', so that object has not yet begun its period of construction and // any polymorphic operation on it results in undefined behavior. Info.FFDiag(E); - return None; + return std::nullopt; } /// Perform virtual dispatch. static const CXXMethodDecl *HandleVirtualDispatch( EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found, llvm::SmallVectorImpl<QualType> &CovariantAdjustmentPath) { - Optional<DynamicType> DynType = ComputeDynamicType( + std::optional<DynamicType> DynType = ComputeDynamicType( Info, E, This, isa<CXXDestructorDecl>(Found) ? AK_Destroy : AK_MemberCall); if (!DynType) @@ -5839,7 +5847,7 @@ static bool HandleDynamicCast(EvalInfo &Info, const ExplicitCastExpr *E, // For all the other cases, we need the pointer to point to an object within // its lifetime / period of construction / destruction, and we need to know // its dynamic type. - Optional<DynamicType> DynType = + std::optional<DynamicType> DynType = ComputeDynamicType(Info, E, Ptr, AK_DynamicCast); if (!DynType) return false; @@ -6728,10 +6736,10 @@ static const FunctionDecl *getVirtualOperatorDelete(QualType T) { /// still exists and is of the right kind for the purpose of a deletion. /// /// On success, returns the heap allocation to deallocate. On failure, produces -/// a diagnostic and returns None. -static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E, - const LValue &Pointer, - DynAlloc::Kind DeallocKind) { +/// a diagnostic and returns std::nullopt. +static std::optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E, + const LValue &Pointer, + DynAlloc::Kind DeallocKind) { auto PointerAsString = [&] { return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy); }; @@ -6742,13 +6750,13 @@ static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E, << PointerAsString(); if (Pointer.Base) NoteLValueLocation(Info, Pointer.Base); - return None; + return std::nullopt; } - Optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA); + std::optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA); if (!Alloc) { Info.FFDiag(E, diag::note_constexpr_double_delete); - return None; + return std::nullopt; } QualType AllocType = Pointer.Base.getDynamicAllocType(); @@ -6756,7 +6764,7 @@ static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E, Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) << DeallocKind << (*Alloc)->getKind() << AllocType; NoteLValueLocation(Info, Pointer.Base); - return None; + return std::nullopt; } bool Subobject = false; @@ -6770,7 +6778,7 @@ static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E, if (Subobject) { Info.FFDiag(E, diag::note_constexpr_delete_subobject) << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); - return None; + return std::nullopt; } return Alloc; @@ -6822,7 +6830,7 @@ class BitCastBuffer { // FIXME: Its possible under the C++ standard for 'char' to not be 8 bits, but // we don't support a host or target where that is the case. Still, we should // use a more generic type in case we ever do. - SmallVector<Optional<unsigned char>, 32> Bytes; + SmallVector<std::optional<unsigned char>, 32> Bytes; static_assert(std::numeric_limits<unsigned char>::digits >= 8, "Need at least 8 bit unsigned char"); @@ -6834,9 +6842,8 @@ public: : Bytes(Width.getQuantity()), TargetIsLittleEndian(TargetIsLittleEndian) {} - LLVM_NODISCARD - bool readObject(CharUnits Offset, CharUnits Width, - SmallVectorImpl<unsigned char> &Output) const { + [[nodiscard]] bool readObject(CharUnits Offset, CharUnits Width, + SmallVectorImpl<unsigned char> &Output) const { for (CharUnits I = Offset, E = Offset + Width; I != E; ++I) { // If a byte of an integer is uninitialized, then the whole integer is // uninitialized. @@ -7013,12 +7020,12 @@ class APValueToBufferConverter { } public: - static Optional<BitCastBuffer> convert(EvalInfo &Info, const APValue &Src, - const CastExpr *BCE) { + static std::optional<BitCastBuffer> + convert(EvalInfo &Info, const APValue &Src, const CastExpr *BCE) { CharUnits DstSize = Info.Ctx.getTypeSizeInChars(BCE->getType()); APValueToBufferConverter Converter(Info, DstSize, BCE); if (!Converter.visit(Src, BCE->getSubExpr()->getType())) - return None; + return std::nullopt; return Converter.Buffer; } }; @@ -7036,22 +7043,22 @@ class BufferToAPValueConverter { // Emit an unsupported bit_cast type error. Sema refuses to build a bit_cast // with an invalid type, so anything left is a deficiency on our part (FIXME). // Ideally this will be unreachable. - llvm::NoneType unsupportedType(QualType Ty) { + std::nullopt_t unsupportedType(QualType Ty) { Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << Ty; - return None; + return std::nullopt; } - llvm::NoneType unrepresentableValue(QualType Ty, const APSInt &Val) { + std::nullopt_t unrepresentableValue(QualType Ty, const APSInt &Val) { Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unrepresentable_value) << Ty << toString(Val, /*Radix=*/10); - return None; + return std::nullopt; } - Optional<APValue> visit(const BuiltinType *T, CharUnits Offset, - const EnumType *EnumSugar = nullptr) { + std::optional<APValue> visit(const BuiltinType *T, CharUnits Offset, + const EnumType *EnumSugar = nullptr) { if (T->isNullPtrType()) { uint64_t NullValue = Info.Ctx.getTargetNullPointerValue(QualType(T, 0)); return APValue((Expr *)nullptr, @@ -7087,7 +7094,7 @@ class BufferToAPValueConverter { Info.FFDiag(BCE->getExprLoc(), diag::note_constexpr_bit_cast_indet_dest) << DisplayType << Info.Ctx.getLangOpts().CharIsSigned; - return None; + return std::nullopt; } return APValue::IndeterminateValue(); @@ -7119,7 +7126,7 @@ class BufferToAPValueConverter { return unsupportedType(QualType(T, 0)); } - Optional<APValue> visit(const RecordType *RTy, CharUnits Offset) { + std::optional<APValue> visit(const RecordType *RTy, CharUnits Offset) { const RecordDecl *RD = RTy->getAsRecordDecl(); const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); @@ -7139,10 +7146,10 @@ class BufferToAPValueConverter { Info.Ctx.getASTRecordLayout(BaseDecl).getNonVirtualSize().isZero()) continue; - Optional<APValue> SubObj = visitType( + std::optional<APValue> SubObj = visitType( BS.getType(), Layout.getBaseClassOffset(BaseDecl) + Offset); if (!SubObj) - return None; + return std::nullopt; ResultVal.getStructBase(I) = *SubObj; } } @@ -7155,7 +7162,7 @@ class BufferToAPValueConverter { if (FD->isBitField()) { Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_bitfield); - return None; + return std::nullopt; } uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); @@ -7165,9 +7172,9 @@ class BufferToAPValueConverter { CharUnits::fromQuantity(FieldOffsetBits / Info.Ctx.getCharWidth()) + Offset; QualType FieldTy = FD->getType(); - Optional<APValue> SubObj = visitType(FieldTy, FieldOffset); + std::optional<APValue> SubObj = visitType(FieldTy, FieldOffset); if (!SubObj) - return None; + return std::nullopt; ResultVal.getStructField(FieldIdx) = *SubObj; ++FieldIdx; } @@ -7175,7 +7182,7 @@ class BufferToAPValueConverter { return ResultVal; } - Optional<APValue> visit(const EnumType *Ty, CharUnits Offset) { + std::optional<APValue> visit(const EnumType *Ty, CharUnits Offset) { QualType RepresentationType = Ty->getDecl()->getIntegerType(); assert(!RepresentationType.isNull() && "enum forward decl should be caught by Sema"); @@ -7186,27 +7193,27 @@ class BufferToAPValueConverter { return visit(AsBuiltin, Offset, /*EnumTy=*/Ty); } - Optional<APValue> visit(const ConstantArrayType *Ty, CharUnits Offset) { + std::optional<APValue> visit(const ConstantArrayType *Ty, CharUnits Offset) { size_t Size = Ty->getSize().getLimitedValue(); CharUnits ElementWidth = Info.Ctx.getTypeSizeInChars(Ty->getElementType()); APValue ArrayValue(APValue::UninitArray(), Size, Size); for (size_t I = 0; I != Size; ++I) { - Optional<APValue> ElementValue = + std::optional<APValue> ElementValue = visitType(Ty->getElementType(), Offset + I * ElementWidth); if (!ElementValue) - return None; + return std::nullopt; ArrayValue.getArrayInitializedElt(I) = std::move(*ElementValue); } return ArrayValue; } - Optional<APValue> visit(const Type *Ty, CharUnits Offset) { + std::optional<APValue> visit(const Type *Ty, CharUnits Offset) { return unsupportedType(QualType(Ty, 0)); } - Optional<APValue> visitType(QualType Ty, CharUnits Offset) { + std::optional<APValue> visitType(QualType Ty, CharUnits Offset) { QualType Can = Ty.getCanonicalType(); switch (Can->getTypeClass()) { @@ -7231,8 +7238,8 @@ class BufferToAPValueConverter { public: // Pull out a full value of type DstType. - static Optional<APValue> convert(EvalInfo &Info, BitCastBuffer &Buffer, - const CastExpr *BCE) { + static std::optional<APValue> convert(EvalInfo &Info, BitCastBuffer &Buffer, + const CastExpr *BCE) { BufferToAPValueConverter Converter(Info, Buffer, BCE); return Converter.visitType(BCE->getType(), CharUnits::fromQuantity(0)); } @@ -7321,13 +7328,13 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, return false; // Read out SourceValue into a char buffer. - Optional<BitCastBuffer> Buffer = + std::optional<BitCastBuffer> Buffer = APValueToBufferConverter::convert(Info, SourceRValue, BCE); if (!Buffer) return false; // Write out the buffer into a new APValue. - Optional<APValue> MaybeDestValue = + std::optional<APValue> MaybeDestValue = BufferToAPValueConverter::convert(Info, *Buffer, BCE); if (!MaybeDestValue) return false; @@ -7406,6 +7413,12 @@ protected: bool ZeroInitialization(const Expr *E) { return Error(E); } + bool IsConstantEvaluatedBuiltinCall(const CallExpr *E) { + unsigned BuiltinOp = E->getBuiltinCallee(); + return BuiltinOp != 0 && + Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp); + } + public: ExprEvaluatorBase(EvalInfo &Info) : Info(Info) {} @@ -7613,7 +7626,7 @@ public: const FunctionDecl *FD = nullptr; LValue *This = nullptr, ThisVal; - auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs()); + auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs()); bool HasQualifier = false; CallRef Call; @@ -7945,8 +7958,8 @@ public: bool VisitStmtExpr(const StmtExpr *E) { // We will have checked the full-expressions inside the statement expression // when they were completed, and don't need to check them again now. - llvm::SaveAndRestore<bool> NotCheckingForUB( - Info.CheckingForUndefinedBehavior, false); + llvm::SaveAndRestore NotCheckingForUB(Info.CheckingForUndefinedBehavior, + false); const CompoundStmt *CS = E->getSubStmt(); if (CS->body_empty()) @@ -8179,7 +8192,8 @@ public: return LValueExprEvaluatorBaseTy::VisitCastExpr(E); case CK_LValueBitCast: - this->CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + this->CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << Info.Ctx.getLangOpts().CPlusPlus; if (!Visit(E->getSubExpr())) return false; Result.Designator.setInvalid(); @@ -8208,7 +8222,7 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, bool InvalidBaseOK) { assert(!E->isValueDependent()); assert(E->isGLValue() || E->getType()->isFunctionType() || - E->getType()->isVoidType() || isa<ObjCSelectorExpr>(E)); + E->getType()->isVoidType() || isa<ObjCSelectorExpr>(E->IgnoreParens())); return LValueExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); } @@ -8317,7 +8331,12 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { } bool LValueExprEvaluator::VisitCallExpr(const CallExpr *E) { + if (!IsConstantEvaluatedBuiltinCall(E)) + return ExprEvaluatorBaseTy::VisitCallExpr(E); + switch (E->getBuiltinCallee()) { + default: + return false; case Builtin::BIas_const: case Builtin::BIforward: case Builtin::BImove: @@ -8424,7 +8443,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { if (!Visit(E->getExprOperand())) return false; - Optional<DynamicType> DynType = + std::optional<DynamicType> DynType = ComputeDynamicType(Info, E, Result, AK_TypeId); if (!DynType) return false; @@ -8890,9 +8909,10 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { Result.Designator.setInvalid(); if (SubExpr->getType()->isVoidPointerType()) CCEDiag(E, diag::note_constexpr_invalid_cast) - << 3 << SubExpr->getType(); + << 3 << SubExpr->getType(); else - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << Info.Ctx.getLangOpts().CPlusPlus; } } if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) @@ -8929,7 +8949,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { return ZeroInitialization(E); case CK_IntegralToPointer: { - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << Info.Ctx.getLangOpts().CPlusPlus; APValue Value; if (!EvaluateIntegerOrLValue(SubExpr, Value, Info)) @@ -9090,13 +9111,9 @@ bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) { } bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { - if (IsConstantCall(E)) - return Success(E); - - if (unsigned BuiltinOp = E->getBuiltinCallee()) - return VisitBuiltinCallExpr(E, BuiltinOp); - - return visitNonBuiltinCallExpr(E); + if (!IsConstantEvaluatedBuiltinCall(E)) + return visitNonBuiltinCallExpr(E); + return VisitBuiltinCallExpr(E, E->getBuiltinCallee()); } // Determine if T is a character type for which we guarantee that @@ -9107,6 +9124,9 @@ static bool isOneByteCharacterType(QualType T) { bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { + if (IsNoOpCall(E)) + return Success(E); + switch (BuiltinOp) { case Builtin::BIaddressof: case Builtin::BI__addressof: @@ -9213,11 +9233,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BIwmemchr: if (Info.getLangOpts().CPlusPlus11) Info.CCEDiag(E, diag::note_constexpr_invalid_function) - << /*isConstexpr*/0 << /*isConstructor*/0 - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'"); + << /*isConstexpr*/ 0 << /*isConstructor*/ 0 + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str(); else Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BI__builtin_strchr: case Builtin::BI__builtin_wcschr: case Builtin::BI__builtin_memchr: @@ -9259,7 +9279,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // FIXME: We can compare the bytes in the correct order. if (IsRawByte && !isOneByteCharacterType(CharTy)) { Info.FFDiag(E, diag::note_constexpr_memchr_unsupported) - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'") + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str() << CharTy; return false; } @@ -9278,7 +9298,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, Desired)) return ZeroInitialization(E); StopAtNull = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BImemchr: case Builtin::BI__builtin_memchr: case Builtin::BI__builtin_char_memchr: @@ -9291,7 +9311,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BIwcschr: case Builtin::BI__builtin_wcschr: StopAtNull = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BIwmemchr: case Builtin::BI__builtin_wmemchr: // wcschr and wmemchr are given a wchar_t to look for. Just use it. @@ -9321,11 +9341,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BIwmemmove: if (Info.getLangOpts().CPlusPlus11) Info.CCEDiag(E, diag::note_constexpr_invalid_function) - << /*isConstexpr*/0 << /*isConstructor*/0 - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'"); + << /*isConstexpr*/ 0 << /*isConstructor*/ 0 + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str(); else Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BI__builtin_memcpy: case Builtin::BI__builtin_memmove: case Builtin::BI__builtin_wmemcpy: @@ -9461,10 +9481,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } default: - break; + return false; } - - return visitNonBuiltinCallExpr(E); } static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, @@ -9527,7 +9545,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { bool ValueInit = false; QualType AllocType = E->getAllocatedType(); - if (Optional<const Expr *> ArraySize = E->getArraySize()) { + if (std::optional<const Expr *> ArraySize = E->getArraySize()) { const Expr *Stripped = *ArraySize; for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped); Stripped = ICE->getSubExpr()) @@ -9809,6 +9827,9 @@ namespace { bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T); bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E); bool VisitBinCmp(const BinaryOperator *E); + bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); + bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit, + ArrayRef<Expr *> Args); }; } @@ -9927,8 +9948,13 @@ bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) { bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { if (E->isTransparent()) return Visit(E->getInit(0)); + return VisitCXXParenListOrInitListExpr(E, E->inits()); +} - const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); +bool RecordExprEvaluator::VisitCXXParenListOrInitListExpr( + const Expr *ExprToVisit, ArrayRef<Expr *> Args) { + const RecordDecl *RD = + ExprToVisit->getType()->castAs<RecordType>()->getDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); @@ -9939,7 +9965,16 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { CXXRD && CXXRD->getNumBases()); if (RD->isUnion()) { - const FieldDecl *Field = E->getInitializedFieldInUnion(); + const FieldDecl *Field; + if (auto *ILE = dyn_cast<InitListExpr>(ExprToVisit)) { + Field = ILE->getInitializedFieldInUnion(); + } else if (auto *PLIE = dyn_cast<CXXParenListInitExpr>(ExprToVisit)) { + Field = PLIE->getInitializedFieldInUnion(); + } else { + llvm_unreachable( + "Expression is neither an init list nor a C++ paren list"); + } + Result = APValue(Field); if (!Field) return true; @@ -9950,7 +9985,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // Is this difference ever observable for initializer lists which // we don't build? ImplicitValueInitExpr VIE(Field->getType()); - const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; + const Expr *InitExpr = Args.empty() ? &VIE : Args[0]; LValue Subobject = This; if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) @@ -9979,8 +10014,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // Initialize base classes. if (CXXRD && CXXRD->getNumBases()) { for (const auto &Base : CXXRD->bases()) { - assert(ElementNo < E->getNumInits() && "missing init for base class"); - const Expr *Init = E->getInit(ElementNo); + assert(ElementNo < Args.size() && "missing init for base class"); + const Expr *Init = Args[ElementNo]; LValue Subobject = This; if (!HandleLValueBase(Info, Init, Subobject, CXXRD, &Base)) @@ -10007,18 +10042,18 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { LValue Subobject = This; - bool HaveInit = ElementNo < E->getNumInits(); + bool HaveInit = ElementNo < Args.size(); // FIXME: Diagnostics here should point to the end of the initializer // list, not the start. - if (!HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, + if (!HandleLValueMember(Info, HaveInit ? Args[ElementNo] : ExprToVisit, Subobject, Field, &Layout)) return false; // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); - const Expr *Init = HaveInit ? E->getInit(ElementNo++) : &VIE; + const Expr *Init = HaveInit ? Args[ElementNo++] : &VIE; if (Field->getType()->isIncompleteArrayType()) { if (auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType())) { @@ -10093,7 +10128,7 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (ZeroInit && !ZeroInitialization(E, T)) return false; - auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs()); + auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs()); return HandleConstructorCall(E, This, Args, cast<CXXConstructorDecl>(Definition), Info, Result); @@ -10504,10 +10539,10 @@ bool VectorExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { return Success(LHSValue, E); } -static llvm::Optional<APValue> handleVectorUnaryOperator(ASTContext &Ctx, - QualType ResultTy, - UnaryOperatorKind Op, - APValue Elt) { +static std::optional<APValue> handleVectorUnaryOperator(ASTContext &Ctx, + QualType ResultTy, + UnaryOperatorKind Op, + APValue Elt) { switch (Op) { case UO_Plus: // Nothing to do here. @@ -10550,7 +10585,7 @@ static llvm::Optional<APValue> handleVectorUnaryOperator(ASTContext &Ctx, } default: // FIXME: Implement the rest of the unary operators. - return llvm::None; + return std::nullopt; } } @@ -10581,7 +10616,7 @@ bool VectorExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { SmallVector<APValue, 4> ResultElements; for (unsigned EltNum = 0; EltNum < VD->getNumElements(); ++EltNum) { - llvm::Optional<APValue> Elt = handleVectorUnaryOperator( + std::optional<APValue> Elt = handleVectorUnaryOperator( Info.Ctx, ResultEltTy, Op, SubExprValue.getVectorElt(EltNum)); if (!Elt) return false; @@ -10652,6 +10687,11 @@ namespace { expandStringLiteral(Info, E, Result, AllocType); return true; } + bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); + bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit, + ArrayRef<Expr *> Args, + const Expr *ArrayFiller, + QualType AllocType = QualType()); }; } // end anonymous namespace @@ -10695,6 +10735,11 @@ static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { if (MaybeElementDependentArrayFiller(ILE->getInit(I))) return true; } + + if (ILE->hasArrayFiller() && + MaybeElementDependentArrayFiller(ILE->getArrayFiller())) + return true; + return false; } return true; @@ -10722,6 +10767,16 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, assert(!E->isTransparent() && "transparent array list initialization is not string literal init?"); + return VisitCXXParenListOrInitListExpr(E, E->inits(), E->getArrayFiller(), + AllocType); +} + +bool ArrayExprEvaluator::VisitCXXParenListOrInitListExpr( + const Expr *ExprToVisit, ArrayRef<Expr *> Args, const Expr *ArrayFiller, + QualType AllocType) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? ExprToVisit->getType() : AllocType); + bool Success = true; assert((!Result.isArray() || Result.getArrayInitializedElts() == 0) && @@ -10730,13 +10785,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, if (Result.isArray() && Result.hasArrayFiller()) Filler = Result.getArrayFiller(); - unsigned NumEltsToInit = E->getNumInits(); + unsigned NumEltsToInit = Args.size(); unsigned NumElts = CAT->getSize().getZExtValue(); - const Expr *FillerExpr = E->hasArrayFiller() ? E->getArrayFiller() : nullptr; // If the initializer might depend on the array index, run it for each // array element. - if (NumEltsToInit != NumElts && MaybeElementDependentArrayFiller(FillerExpr)) + if (NumEltsToInit != NumElts && MaybeElementDependentArrayFiller(ArrayFiller)) NumEltsToInit = NumElts; LLVM_DEBUG(llvm::dbgs() << "The number of elements to initialize: " @@ -10754,10 +10808,9 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, } LValue Subobject = This; - Subobject.addArray(Info, E, CAT); + Subobject.addArray(Info, ExprToVisit, CAT); for (unsigned Index = 0; Index != NumEltsToInit; ++Index) { - const Expr *Init = - Index < E->getNumInits() ? E->getInit(Index) : FillerExpr; + const Expr *Init = Index < Args.size() ? Args[Index] : ArrayFiller; if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), Info, Subobject, Init) || !HandleLValueArrayAdjustment(Info, Init, Subobject, @@ -10773,9 +10826,10 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, // If we get here, we have a trivial filler, which we can just evaluate // once and splat over the rest of the array elements. - assert(FillerExpr && "no array filler for incomplete init list"); + assert(ArrayFiller && "no array filler for incomplete init list"); return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, - FillerExpr) && Success; + ArrayFiller) && + Success; } bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { @@ -10833,6 +10887,9 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (FinalSize == 0) return true; + bool HasTrivialConstructor = CheckTrivialDefaultConstructor( + Info, E->getExprLoc(), E->getConstructor(), + E->requiresZeroInitialization()); LValue ArrayElt = Subobject; ArrayElt.addArray(Info, E, CAT); // We do the whole initialization in two passes, first for just one element, @@ -10856,19 +10913,26 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, for (unsigned I = OldElts; I < N; ++I) Value->getArrayInitializedElt(I) = Filler; - // Initialize the elements. - for (unsigned I = OldElts; I < N; ++I) { - if (!VisitCXXConstructExpr(E, ArrayElt, - &Value->getArrayInitializedElt(I), - CAT->getElementType()) || - !HandleLValueArrayAdjustment(Info, E, ArrayElt, - CAT->getElementType(), 1)) - return false; - // When checking for const initilization any diagnostic is considered - // an error. - if (Info.EvalStatus.Diag && !Info.EvalStatus.Diag->empty() && - !Info.keepEvaluatingAfterFailure()) - return false; + if (HasTrivialConstructor && N == FinalSize) { + // If we have a trivial constructor, only evaluate it once and copy + // the result into all the array elements. + APValue &FirstResult = Value->getArrayInitializedElt(0); + for (unsigned I = OldElts; I < FinalSize; ++I) + Value->getArrayInitializedElt(I) = FirstResult; + } else { + for (unsigned I = OldElts; I < N; ++I) { + if (!VisitCXXConstructExpr(E, ArrayElt, + &Value->getArrayInitializedElt(I), + CAT->getElementType()) || + !HandleLValueArrayAdjustment(Info, E, ArrayElt, + CAT->getElementType(), 1)) + return false; + // When checking for const initilization any diagnostic is considered + // an error. + if (Info.EvalStatus.Diag && !Info.EvalStatus.Diag->empty() && + !Info.keepEvaluatingAfterFailure()) + return false; + } } } @@ -10882,6 +10946,15 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, .VisitCXXConstructExpr(E, Type); } +bool ArrayExprEvaluator::VisitCXXParenListInitExpr( + const CXXParenListInitExpr *E) { + assert(dyn_cast<ConstantArrayType>(E->getType()) && + "Expression result is not a constant array type"); + + return VisitCXXParenListOrInitListExpr(E, E->getInitExprs(), + E->getArrayFiller()); +} + //===----------------------------------------------------------------------===// // Integer Evaluation // @@ -11596,15 +11669,31 @@ static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const LValue &LVal) { // conservative with the last element in structs (if it's an array), so our // current behavior is more compatible than an explicit list approach would // be. - int StrictFlexArraysLevel = Ctx.getLangOpts().StrictFlexArrays; + auto isFlexibleArrayMember = [&] { + using FAMKind = LangOptions::StrictFlexArraysLevelKind; + FAMKind StrictFlexArraysLevel = + Ctx.getLangOpts().getStrictFlexArraysLevel(); + + if (Designator.isMostDerivedAnUnsizedArray()) + return true; + + if (StrictFlexArraysLevel == FAMKind::Default) + return true; + + if (Designator.getMostDerivedArraySize() == 0 && + StrictFlexArraysLevel != FAMKind::IncompleteOnly) + return true; + + if (Designator.getMostDerivedArraySize() == 1 && + StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete) + return true; + + return false; + }; + return LVal.InvalidBase && Designator.Entries.size() == Designator.MostDerivedPathLength && - Designator.MostDerivedIsArrayElement && - (Designator.isMostDerivedAnUnsizedArray() || - Designator.getMostDerivedArraySize() == 0 || - (Designator.getMostDerivedArraySize() == 1 && - StrictFlexArraysLevel < 2) || - StrictFlexArraysLevel == 0) && + Designator.MostDerivedIsArrayElement && isFlexibleArrayMember() && isDesignatorAtObjectEnd(Ctx, LVal); } @@ -11751,10 +11840,9 @@ static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type, } bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { - if (unsigned BuiltinOp = E->getBuiltinCallee()) - return VisitBuiltinCallExpr(E, BuiltinOp); - - return ExprEvaluatorBaseTy::VisitCallExpr(E); + if (!IsConstantEvaluatedBuiltinCall(E)) + return ExprEvaluatorBaseTy::VisitCallExpr(E); + return VisitBuiltinCallExpr(E, E->getBuiltinCallee()); } static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info, @@ -11788,7 +11876,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { switch (BuiltinOp) { default: - return ExprEvaluatorBaseTy::VisitCallExpr(E); + return false; case Builtin::BI__builtin_dynamic_object_size: case Builtin::BI__builtin_object_size: { @@ -12103,11 +12191,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // A call to strlen is not a constant expression. if (Info.getLangOpts().CPlusPlus11) Info.CCEDiag(E, diag::note_constexpr_invalid_function) - << /*isConstexpr*/0 << /*isConstructor*/0 - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'"); + << /*isConstexpr*/ 0 << /*isConstructor*/ 0 + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str(); else Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BI__builtin_strlen: case Builtin::BI__builtin_wcslen: { // As an extension, we support __builtin_strlen() as a constant expression, @@ -12128,11 +12216,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // A call to strlen is not a constant expression. if (Info.getLangOpts().CPlusPlus11) Info.CCEDiag(E, diag::note_constexpr_invalid_function) - << /*isConstexpr*/0 << /*isConstructor*/0 - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'"); + << /*isConstexpr*/ 0 << /*isConstructor*/ 0 + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str(); else Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); - LLVM_FALLTHROUGH; + [[fallthrough]]; case Builtin::BI__builtin_strcmp: case Builtin::BI__builtin_wcscmp: case Builtin::BI__builtin_strncmp: @@ -12184,7 +12272,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, !(isOneByteCharacterType(CharTy1) && isOneByteCharacterType(CharTy2))) { // FIXME: Consider using our bit_cast implementation to support this. Info.FFDiag(E, diag::note_constexpr_memcmp_unsupported) - << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'") + << ("'" + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'").str() << CharTy1 << CharTy2; return false; } @@ -12886,41 +12974,55 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // Reject differing bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { + auto DiagComparison = [&] (unsigned DiagID, bool Reversed = false) { + std::string LHS = LHSValue.toString(Info.Ctx, E->getLHS()->getType()); + std::string RHS = RHSValue.toString(Info.Ctx, E->getRHS()->getType()); + Info.FFDiag(E, DiagID) + << (Reversed ? RHS : LHS) << (Reversed ? LHS : RHS); + return false; + }; // Inequalities and subtractions between unrelated pointers have // unspecified or undefined behavior. - if (!IsEquality) { - Info.FFDiag(E, diag::note_constexpr_pointer_comparison_unspecified); - return false; - } + if (!IsEquality) + return DiagComparison( + diag::note_constexpr_pointer_comparison_unspecified); // A constant address may compare equal to the address of a symbol. // The one exception is that address of an object cannot compare equal // to a null pointer constant. + // TODO: Should we restrict this to actual null pointers, and exclude the + // case of zero cast to pointer type? if ((!LHSValue.Base && !LHSValue.Offset.isZero()) || (!RHSValue.Base && !RHSValue.Offset.isZero())) - return Error(E); + return DiagComparison(diag::note_constexpr_pointer_constant_comparison, + !RHSValue.Base); // It's implementation-defined whether distinct literals will have // distinct addresses. In clang, the result of such a comparison is // unspecified, so it is not a constant expression. However, we do know // that the address of a literal will be non-null. if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) && LHSValue.Base && RHSValue.Base) - return Error(E); + return DiagComparison(diag::note_constexpr_literal_comparison); // We can't tell whether weak symbols will end up pointing to the same // object. if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue)) - return Error(E); + return DiagComparison(diag::note_constexpr_pointer_weak_comparison, + !IsWeakLValue(LHSValue)); // We can't compare the address of the start of one object with the // past-the-end address of another object, per C++ DR1652. - if ((LHSValue.Base && LHSValue.Offset.isZero() && - isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) || - (RHSValue.Base && RHSValue.Offset.isZero() && - isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue))) - return Error(E); + if (LHSValue.Base && LHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) + return DiagComparison(diag::note_constexpr_pointer_comparison_past_end, + true); + if (RHSValue.Base && RHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)) + return DiagComparison(diag::note_constexpr_pointer_comparison_past_end, + false); // We can't tell whether an object is at the same address as another // zero sized object. if ((RHSValue.Base && isZeroSized(LHSValue)) || (LHSValue.Base && isZeroSized(RHSValue))) - return Error(E); + return DiagComparison( + diag::note_constexpr_pointer_comparison_zero_sized); return Success(CmpResult::Unequal, E); } @@ -13024,6 +13126,19 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!EvaluateMemberPointer(E->getRHS(), RHSValue, Info) || !LHSOK) return false; + // If either operand is a pointer to a weak function, the comparison is not + // constant. + if (LHSValue.getDecl() && LHSValue.getDecl()->isWeak()) { + Info.FFDiag(E, diag::note_constexpr_mem_pointer_weak_comparison) + << LHSValue.getDecl(); + return true; + } + if (RHSValue.getDecl() && RHSValue.getDecl()->isWeak()) { + Info.FFDiag(E, diag::note_constexpr_mem_pointer_weak_comparison) + << RHSValue.getDecl(); + return true; + } + // C++11 [expr.eq]p2: // If both operands are null, they compare equal. Otherwise if only one is // null, they compare unequal. @@ -13101,6 +13216,11 @@ bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) { }); } +bool RecordExprEvaluator::VisitCXXParenListInitExpr( + const CXXParenListInitExpr *E) { + return VisitCXXParenListOrInitListExpr(E, E->getInitExprs()); +} + bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { // We don't support assignment in C. C++ assignments don't get here because // assignment is an lvalue in C++. @@ -13519,12 +13639,62 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { return Info.Ctx.getTypeSize(DestType) == Info.Ctx.getTypeSize(SrcType); } + if (Info.Ctx.getLangOpts().CPlusPlus && Info.InConstantContext && + Info.EvalMode == EvalInfo::EM_ConstantExpression && + DestType->isEnumeralType()) { + + bool ConstexprVar = true; + + // We know if we are here that we are in a context that we might require + // a constant expression or a context that requires a constant + // value. But if we are initializing a value we don't know if it is a + // constexpr variable or not. We can check the EvaluatingDecl to determine + // if it constexpr or not. If not then we don't want to emit a diagnostic. + if (const auto *VD = dyn_cast_or_null<VarDecl>( + Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) + ConstexprVar = VD->isConstexpr(); + + const EnumType *ET = dyn_cast<EnumType>(DestType.getCanonicalType()); + const EnumDecl *ED = ET->getDecl(); + // Check that the value is within the range of the enumeration values. + // + // This corressponds to [expr.static.cast]p10 which says: + // A value of integral or enumeration type can be explicitly converted + // to a complete enumeration type ... If the enumeration type does not + // have a fixed underlying type, the value is unchanged if the original + // value is within the range of the enumeration values ([dcl.enum]), and + // otherwise, the behavior is undefined. + // + // This was resolved as part of DR2338 which has CD5 status. + if (!ED->isFixed()) { + llvm::APInt Min; + llvm::APInt Max; + + ED->getValueRange(Max, Min); + --Max; + + if (ED->getNumNegativeBits() && ConstexprVar && + (Max.slt(Result.getInt().getSExtValue()) || + Min.sgt(Result.getInt().getSExtValue()))) + Info.Ctx.getDiagnostics().Report( + E->getExprLoc(), diag::warn_constexpr_unscoped_enum_out_of_range) + << llvm::toString(Result.getInt(), 10) << Min.getSExtValue() + << Max.getSExtValue(); + else if (!ED->getNumNegativeBits() && ConstexprVar && + Max.ult(Result.getInt().getZExtValue())) + Info.Ctx.getDiagnostics().Report(E->getExprLoc(), + diag::warn_constexpr_unscoped_enum_out_of_range) + << llvm::toString(Result.getInt(),10) << Min.getZExtValue() << Max.getZExtValue(); + } + } + return Success(HandleIntToIntCast(Info, E, DestType, SrcType, Result.getInt()), E); } case CK_PointerToIntegral: { - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << Info.Ctx.getLangOpts().CPlusPlus; LValue LV; if (!EvaluatePointer(SubExpr, LV, Info)) @@ -13878,9 +14048,12 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context, } bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { + if (!IsConstantEvaluatedBuiltinCall(E)) + return ExprEvaluatorBaseTy::VisitCallExpr(E); + switch (E->getBuiltinCallee()) { default: - return ExprEvaluatorBaseTy::VisitCallExpr(E); + return false; case Builtin::BI__builtin_huge_val: case Builtin::BI__builtin_huge_valf: @@ -13954,6 +14127,42 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { Result.copySign(RHS); return true; } + + case Builtin::BI__builtin_fmax: + case Builtin::BI__builtin_fmaxf: + case Builtin::BI__builtin_fmaxl: + case Builtin::BI__builtin_fmaxf16: + case Builtin::BI__builtin_fmaxf128: { + // TODO: Handle sNaN. + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + // When comparing zeroes, return +0.0 if one of the zeroes is positive. + if (Result.isZero() && RHS.isZero() && Result.isNegative()) + Result = RHS; + else if (Result.isNaN() || RHS > Result) + Result = RHS; + return true; + } + + case Builtin::BI__builtin_fmin: + case Builtin::BI__builtin_fminf: + case Builtin::BI__builtin_fminl: + case Builtin::BI__builtin_fminf16: + case Builtin::BI__builtin_fminf128: { + // TODO: Handle sNaN. + APFloat RHS(0.); + if (!EvaluateFloat(E->getArg(0), Result, Info) || + !EvaluateFloat(E->getArg(1), RHS, Info)) + return false; + // When comparing zeroes, return -0.0 if one of the zeroes is negative. + if (Result.isZero() && RHS.isZero() && RHS.isNegative()) + Result = RHS; + else if (Result.isNaN() || RHS < Result) + Result = RHS; + return true; + } } } @@ -14564,6 +14773,9 @@ bool ComplexExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } bool ComplexExprEvaluator::VisitCallExpr(const CallExpr *E) { + if (!IsConstantEvaluatedBuiltinCall(E)) + return ExprEvaluatorBaseTy::VisitCallExpr(E); + switch (E->getBuiltinCallee()) { case Builtin::BI__builtin_complex: Result.makeComplexFloat(); @@ -14574,10 +14786,8 @@ bool ComplexExprEvaluator::VisitCallExpr(const CallExpr *E) { return true; default: - break; + return false; } - - return ExprEvaluatorBaseTy::VisitCallExpr(E); } //===----------------------------------------------------------------------===// @@ -14653,6 +14863,9 @@ public: } bool VisitCallExpr(const CallExpr *E) { + if (!IsConstantEvaluatedBuiltinCall(E)) + return ExprEvaluatorBaseTy::VisitCallExpr(E); + switch (E->getBuiltinCallee()) { case Builtin::BI__assume: case Builtin::BI__builtin_assume: @@ -14663,10 +14876,8 @@ public: return HandleOperatorDeleteCall(Info, E); default: - break; + return false; } - - return ExprEvaluatorBaseTy::VisitCallExpr(E); } bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); @@ -14703,7 +14914,7 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { return true; } - Optional<DynAlloc *> Alloc = CheckDeleteKind( + std::optional<DynAlloc *> Alloc = CheckDeleteKind( Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New); if (!Alloc) return false; @@ -14871,25 +15082,27 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { assert(!E->isValueDependent()); + + if (E->getType().isNull()) + return false; + + if (!CheckLiteralType(Info, E)) + return false; + if (Info.EnableNewConstInterp) { if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result)) return false; } else { - if (E->getType().isNull()) - return false; - - if (!CheckLiteralType(Info, E)) - return false; - if (!::Evaluate(Result, Info, E)) return false; + } - if (E->isGLValue()) { - LValue LV; - LV.setFrom(Info.Ctx, Result); - if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) - return false; - } + // Implicit lvalue-to-rvalue cast. + if (E->isGLValue()) { + LValue LV; + LV.setFrom(Info.Ctx, Result); + if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) + return false; } // Check this core constant expression is a constant expression. @@ -14909,6 +15122,12 @@ static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result, return true; } + if (const auto *L = dyn_cast<CXXBoolLiteralExpr>(Exp)) { + Result.Val = APValue(APSInt(APInt(1, L->getValue()))); + IsConst = true; + return true; + } + // This case should be rare, but we need to check it before we check on // the type below. if (Exp->getType().isNull()) { @@ -14986,6 +15205,7 @@ bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsRValue"); EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsRValue(this, Result, Ctx, Info); @@ -14995,6 +15215,7 @@ bool Expr::EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsBooleanCondition"); EvalResult Scratch; return EvaluateAsRValue(Scratch, Ctx, InConstantContext) && HandleConversionToBool(Scratch.Val, Result); @@ -15005,6 +15226,7 @@ bool Expr::EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsInt"); EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info); @@ -15015,6 +15237,7 @@ bool Expr::EvaluateAsFixedPoint(EvalResult &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFixedPoint"); EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info); @@ -15029,6 +15252,7 @@ bool Expr::EvaluateAsFloat(APFloat &Result, const ASTContext &Ctx, if (!getType()->isRealFloatingType()) return false; + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFloat"); EvalResult ExprResult; if (!EvaluateAsRValue(ExprResult, Ctx, InConstantContext) || !ExprResult.Val.isFloat() || @@ -15044,6 +15268,7 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue"); EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; @@ -15087,7 +15312,11 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, ConstantExprKind Kind) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + bool IsConst; + if (FastEvaluateAsRValue(this, Result, Ctx, IsConst) && Result.Val.hasValue()) + return true; + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr"); EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; @@ -15140,6 +15369,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + VD->printQualifiedName(OS); + return Name; + }); + // FIXME: Evaluating initializers for large array and record types can cause // performance problems. Only do so in C++11 for now. if (isPRValue() && (getType()->isArrayType() || getType()->isRecordType()) && @@ -15228,6 +15464,7 @@ APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstInt"); EvalResult EVResult; EVResult.Diag = Diag; EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects); @@ -15246,6 +15483,7 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow( assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstIntCheckOverflow"); EvalResult EVResult; EVResult.Diag = Diag; EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects); @@ -15264,6 +15502,7 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateForOverflow"); bool IsConst; EvalResult EVResult; if (!FastEvaluateAsRValue(this, EVResult, Ctx, IsConst)) { @@ -15425,6 +15664,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: case Expr::SYCLUniqueStableNameExprClass: + case Expr::CXXParenListInitExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { @@ -15755,6 +15995,8 @@ bool Expr::isIntegerConstantExpr(const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "isIntegerConstantExpr"); + if (Ctx.getLangOpts().CPlusPlus11) return EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, nullptr, Loc); @@ -15766,12 +16008,12 @@ bool Expr::isIntegerConstantExpr(const ASTContext &Ctx, return true; } -Optional<llvm::APSInt> Expr::getIntegerConstantExpr(const ASTContext &Ctx, - SourceLocation *Loc, - bool isEvaluated) const { +std::optional<llvm::APSInt> +Expr::getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc, + bool isEvaluated) const { if (isValueDependent()) { // Expression evaluator can't succeed on a dependent expression. - return None; + return std::nullopt; } APSInt Value; @@ -15779,11 +16021,11 @@ Optional<llvm::APSInt> Expr::getIntegerConstantExpr(const ASTContext &Ctx, if (Ctx.getLangOpts().CPlusPlus11) { if (EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, &Value, Loc)) return Value; - return None; + return std::nullopt; } if (!isIntegerConstantExpr(Ctx, Loc)) - return None; + return std::nullopt; // The only possible side-effects here are due to UB discovered in the // evaluation (for instance, INT_MAX + 1). In such a case, we are still @@ -15847,6 +16089,14 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + llvm::TimeTraceScope TimeScope("EvaluateWithSubstitution", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + Callee->getNameForDiagnostic(OS, Ctx.getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + Expr::EvalStatus Status; EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated); Info.InConstantContext = true; @@ -15911,6 +16161,14 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, if (FD->isDependentContext()) return true; + llvm::TimeTraceScope TimeScope("isPotentialConstantExpr", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + FD->getNameForDiagnostic(OS, FD->getASTContext().getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + Expr::EvalStatus Status; Status.Diag = &Diags; diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp index 257833182621..090ef02aa422 100644 --- a/clang/lib/AST/ExternalASTSource.cpp +++ b/clang/lib/AST/ExternalASTSource.cpp @@ -20,9 +20,9 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" -#include "llvm/ADT/None.h" #include "llvm/Support/ErrorHandling.h" #include <cstdint> +#include <optional> using namespace clang; @@ -30,9 +30,9 @@ char ExternalASTSource::ID; ExternalASTSource::~ExternalASTSource() = default; -llvm::Optional<ASTSourceDescriptor> +std::optional<ASTSourceDescriptor> ExternalASTSource::getSourceDescriptor(unsigned ID) { - return None; + return std::nullopt; } ExternalASTSource::ExtKind diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index c0879704de4d..c7dee2d421bb 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" #include "llvm/Support/ConvertUTF.h" +#include <optional> using clang::analyze_format_string::ArgType; using clang::analyze_format_string::FormatStringHandler; @@ -348,7 +349,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { return Match; case AnyCharTy: { - if (const EnumType *ETy = argTy->getAs<EnumType>()) { + if (const auto *ETy = argTy->getAs<EnumType>()) { // If the enum is incomplete we know nothing about the underlying type. // Assume that it's 'int'. if (!ETy->getDecl()->isComplete()) @@ -356,17 +357,34 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { argTy = ETy->getDecl()->getIntegerType(); } - if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + if (const auto *BT = argTy->getAs<BuiltinType>()) { + // The types are perfectly matched? switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::UChar: + case BuiltinType::Char_U: + case BuiltinType::Bool: + return Match; + } + // "Partially matched" because of promotions? + if (!Ptr) { + switch (BT->getKind()) { default: break; - case BuiltinType::Char_S: - case BuiltinType::SChar: - case BuiltinType::UChar: - case BuiltinType::Char_U: - case BuiltinType::Bool: - return Match; + case BuiltinType::Int: + case BuiltinType::UInt: + return MatchPromotion; + case BuiltinType::Short: + case BuiltinType::UShort: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + return NoMatchPromotionTypeConfusion; + } } + } return NoMatch; } @@ -383,8 +401,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { if (T == argTy) return Match; - // Check for "compatible types". - if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + if (const auto *BT = argTy->getAs<BuiltinType>()) { + // Check if the only difference between them is signed vs unsigned + // if true, we consider they are compatible. switch (BT->getKind()) { default: break; @@ -395,25 +414,66 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::Bool: if (T == C.UnsignedShortTy || T == C.ShortTy) return NoMatchTypeConfusion; - return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match - : NoMatch; + if (T == C.UnsignedCharTy || T == C.SignedCharTy) + return Match; + break; case BuiltinType::Short: - return T == C.UnsignedShortTy ? Match : NoMatch; + if (T == C.UnsignedShortTy) + return Match; + break; case BuiltinType::UShort: - return T == C.ShortTy ? Match : NoMatch; + if (T == C.ShortTy) + return Match; + break; case BuiltinType::Int: - return T == C.UnsignedIntTy ? Match : NoMatch; + if (T == C.UnsignedIntTy) + return Match; + break; case BuiltinType::UInt: - return T == C.IntTy ? Match : NoMatch; + if (T == C.IntTy) + return Match; + break; case BuiltinType::Long: - return T == C.UnsignedLongTy ? Match : NoMatch; + if (T == C.UnsignedLongTy) + return Match; + break; case BuiltinType::ULong: - return T == C.LongTy ? Match : NoMatch; + if (T == C.LongTy) + return Match; + break; case BuiltinType::LongLong: - return T == C.UnsignedLongLongTy ? Match : NoMatch; + if (T == C.UnsignedLongLongTy) + return Match; + break; case BuiltinType::ULongLong: - return T == C.LongLongTy ? Match : NoMatch; - } + if (T == C.LongLongTy) + return Match; + break; + } + // "Partially matched" because of promotions? + if (!Ptr) { + switch (BT->getKind()) { + default: + break; + case BuiltinType::Int: + case BuiltinType::UInt: + if (T == C.SignedCharTy || T == C.UnsignedCharTy || + T == C.ShortTy || T == C.UnsignedShortTy || T == C.WCharTy || + T == C.WideCharTy) + return MatchPromotion; + break; + case BuiltinType::Short: + case BuiltinType::UShort: + if (T == C.SignedCharTy || T == C.UnsignedCharTy) + return NoMatchPromotionTypeConfusion; + break; + case BuiltinType::WChar_U: + case BuiltinType::WChar_S: + if (T != C.WCharTy && T != C.WideCharTy) + return NoMatchPromotionTypeConfusion; + } + } + } return NoMatch; } @@ -451,7 +511,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { if (C.getCanonicalType(argTy).getUnqualifiedType() == WInt) return Match; - QualType PromoArg = argTy->isPromotableIntegerType() + QualType PromoArg = C.isPromotableIntegerType(argTy) ? C.getPromotedIntegerType(argTy) : argTy; PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType(); @@ -624,6 +684,8 @@ analyze_format_string::LengthModifier::toString() const { const char *ConversionSpecifier::toString() const { switch (kind) { + case bArg: return "b"; + case BArg: return "B"; case dArg: return "d"; case DArg: return "D"; case iArg: return "i"; @@ -673,13 +735,13 @@ const char *ConversionSpecifier::toString() const { return nullptr; } -Optional<ConversionSpecifier> +std::optional<ConversionSpecifier> ConversionSpecifier::getStandardSpecifier() const { ConversionSpecifier::Kind NewKind; switch (getKind()) { default: - return None; + return std::nullopt; case DArg: NewKind = dArg; break; @@ -745,7 +807,7 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target, break; } } - LLVM_FALLTHROUGH; + [[fallthrough]]; case LengthModifier::AsChar: case LengthModifier::AsLongLong: case LengthModifier::AsQuad: @@ -753,6 +815,8 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target, case LengthModifier::AsSizeT: case LengthModifier::AsPtrDiff: switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: @@ -908,6 +972,8 @@ bool FormatSpecifier::hasStandardLengthModifier() const { bool FormatSpecifier::hasStandardConversionSpecifier( const LangOptions &LangOpt) const { switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::cArg: case ConversionSpecifier::dArg: case ConversionSpecifier::iArg: @@ -966,7 +1032,8 @@ bool FormatSpecifier::hasStandardLengthConversionCombination() const { return true; } -Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const { +std::optional<LengthModifier> +FormatSpecifier::getCorrectedLengthModifier() const { if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) { if (LM.getKind() == LengthModifier::AsLongDouble || LM.getKind() == LengthModifier::AsQuad) { @@ -976,15 +1043,14 @@ Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const { } } - return None; + return std::nullopt; } bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, LengthModifier &LM) { - assert(isa<TypedefType>(QT) && "Expected a TypedefType"); - const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl(); - - for (;;) { + for (/**/; const auto *TT = QT->getAs<TypedefType>(); + QT = TT->getDecl()->getUnderlyingType()) { + const TypedefNameDecl *Typedef = TT->getDecl(); const IdentifierInfo *Identifier = Typedef->getIdentifier(); if (Identifier->getName() == "size_t") { LM.setKind(LengthModifier::AsSizeT); @@ -1003,12 +1069,6 @@ bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, LM.setKind(LengthModifier::AsPtrDiff); return true; } - - QualType T = Typedef->getUnderlyingType(); - if (!isa<TypedefType>(T)) - break; - - Typedef = cast<TypedefType>(T)->getDecl(); } return false; } diff --git a/clang/lib/AST/Interp/Boolean.h b/clang/lib/AST/Interp/Boolean.h index 2baa717311bc..3122388a49a5 100644 --- a/clang/lib/AST/Interp/Boolean.h +++ b/clang/lib/AST/Interp/Boolean.h @@ -22,7 +22,7 @@ namespace clang { namespace interp { /// Wrapper around boolean types. -class Boolean { +class Boolean final { private: /// Underlying boolean. bool V; @@ -46,9 +46,15 @@ class Boolean { Boolean operator-() const { return Boolean(V); } Boolean operator~() const { return Boolean(true); } - explicit operator unsigned() const { return V; } + explicit operator int8_t() const { return V; } + explicit operator uint8_t() const { return V; } + explicit operator int16_t() const { return V; } + explicit operator uint16_t() const { return V; } + explicit operator int32_t() const { return V; } + explicit operator uint32_t() const { return V; } explicit operator int64_t() const { return V; } explicit operator uint64_t() const { return V; } + explicit operator bool() const { return V; } APSInt toAPSInt() const { return APSInt(APInt(1, static_cast<uint64_t>(V), false), true); @@ -84,9 +90,10 @@ class Boolean { static Boolean min(unsigned NumBits) { return Boolean(false); } static Boolean max(unsigned NumBits) { return Boolean(true); } - template <typename T> - static std::enable_if_t<std::is_integral<T>::value, Boolean> from(T Value) { - return Boolean(Value != 0); + template <typename T> static Boolean from(T Value) { + if constexpr (std::is_integral<T>::value) + return Boolean(Value != 0); + return Boolean(static_cast<decltype(Boolean::V)>(Value) != 0); } template <unsigned SrcBits, bool SrcSign> @@ -134,6 +141,16 @@ class Boolean { *R = Boolean(A.V && B.V); return false; } + + static bool inv(Boolean A, Boolean *R) { + *R = Boolean(!A.V); + return false; + } + + static bool neg(Boolean A, Boolean *R) { + *R = Boolean(A.V); + return false; + } }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) { diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index a69b23fd613c..4633d1e0823b 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -19,51 +19,72 @@ using namespace clang::interp; using APSInt = llvm::APSInt; using Error = llvm::Error; -Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { - // Do not try to compile undefined functions. - if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) - return nullptr; - - // Set up argument indices. - unsigned ParamOffset = 0; - SmallVector<PrimType, 8> ParamTypes; - llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; - - // If the return is not a primitive, a pointer to the storage where the value - // is initialized in is passed as the first argument. - QualType Ty = F->getReturnType(); - if (!Ty->isVoidType() && !Ctx.classify(Ty)) { - ParamTypes.push_back(PT_Ptr); - ParamOffset += align(primSize(PT_Ptr)); - } +Expected<Function *> +ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { + // Function is not defined at all or not yet. We will + // create a Function instance but not compile the body. That + // will (maybe) happen later. + bool HasBody = FuncDecl->hasBody(FuncDecl); + + // Create a handle over the emitted code. + Function *Func = P.getFunction(FuncDecl); + if (!Func) { + // Set up argument indices. + unsigned ParamOffset = 0; + SmallVector<PrimType, 8> ParamTypes; + llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; + + // If the return is not a primitive, a pointer to the storage where the + // value is initialized in is passed as the first argument. See 'RVO' + // elsewhere in the code. + QualType Ty = FuncDecl->getReturnType(); + bool HasRVO = false; + if (!Ty->isVoidType() && !Ctx.classify(Ty)) { + HasRVO = true; + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } + + // If the function decl is a member decl, the next parameter is + // the 'this' pointer. This parameter is pop()ed from the + // InterpStack when calling the function. + bool HasThisPointer = false; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl); + MD && MD->isInstance()) { + HasThisPointer = true; + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } - // Assign descriptors to all parameters. - // Composite objects are lowered to pointers. - for (const ParmVarDecl *PD : F->parameters()) { - PrimType Ty; - if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) { - Ty = *T; - } else { - Ty = PT_Ptr; + // Assign descriptors to all parameters. + // Composite objects are lowered to pointers. + for (const ParmVarDecl *PD : FuncDecl->parameters()) { + PrimType Ty = Ctx.classify(PD->getType()).value_or(PT_Ptr); + Descriptor *Desc = P.createDescriptor(PD, Ty); + ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); + Params.insert({PD, ParamOffset}); + ParamOffset += align(primSize(Ty)); + ParamTypes.push_back(Ty); } - Descriptor *Desc = P.createDescriptor(PD, Ty); - ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); - Params.insert({PD, ParamOffset}); - ParamOffset += align(primSize(Ty)); - ParamTypes.push_back(Ty); + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), HasThisPointer, HasRVO); } - // Create a handle over the emitted code. - Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors)); + assert(Func); + if (!HasBody) + return Func; + // Compile the function body. - if (!F->isConstexpr() || !visitFunc(F)) { + if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) { // Return a dummy function if compilation failed. if (BailLocation) return llvm::make_error<ByteCodeGenError>(*BailLocation); - else + else { + Func->setIsFullyCompiled(true); return Func; + } } else { // Create scopes from descriptors. llvm::SmallVector<Scope, 2> Scopes; @@ -74,6 +95,7 @@ Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { // Set the function's code. Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), std::move(Scopes)); + Func->setIsFullyCompiled(true); return Func; } } @@ -94,7 +116,8 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { using namespace llvm::support; /// Rewrite the operand of all jumps to this label. - void *Location = Code.data() + Reloc - sizeof(int32_t); + void *Location = Code.data() + Reloc - align(sizeof(int32_t)); + assert(aligned(Location)); const int32_t Offset = Target - static_cast<int64_t>(Reloc); endian::write<int32_t, endianness::native, 1>(Location, Offset); } @@ -104,7 +127,9 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { int32_t ByteCodeEmitter::getOffset(LabelTy Label) { // Compute the PC offset which the jump is relative to. - const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); + const int64_t Position = + Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t)); + assert(aligned(Position)); // If target is known, compute jump offset. auto It = LabelOffsets.find(Label); @@ -126,30 +151,32 @@ bool ByteCodeEmitter::bail(const SourceLocation &Loc) { /// Helper to write bytecode and bail out if 32-bit offsets become invalid. /// Pointers will be automatically marshalled as 32-bit IDs. template <typename T> -static std::enable_if_t<!std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(Val); - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { - Success = false; - return; - } +static void emit(Program &P, std::vector<char> &Code, const T &Val, + bool &Success) { + size_t Size; - const char *Data = reinterpret_cast<const char *>(&Val); - Code.insert(Code.end(), Data, Data + Size); -} + if constexpr (std::is_pointer_v<T>) + Size = sizeof(uint32_t); + else + Size = sizeof(T); -template <typename T> -static std::enable_if_t<std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(uint32_t); if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { Success = false; return; } - uint32_t ID = P.getOrCreateNativePointer(Val); - const char *Data = reinterpret_cast<const char *>(&ID); - Code.insert(Code.end(), Data, Data + Size); + // Access must be aligned! + size_t ValPos = align(Code.size()); + Size = align(Size); + assert(aligned(ValPos + Size)); + Code.resize(ValPos + Size); + + if constexpr (!std::is_pointer_v<T>) { + new (Code.data() + ValPos) T(Val); + } else { + uint32_t ID = P.getOrCreateNativePointer(Val); + new (Code.data() + ValPos) uint32_t(ID); + } } template <typename... Tys> diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h index 03452a350c96..30da06b20250 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.h +++ b/clang/lib/AST/Interp/ByteCodeEmitter.h @@ -37,7 +37,7 @@ protected: public: /// Compiles the function into the module. - llvm::Expected<Function *> compileFunc(const FunctionDecl *F); + llvm::Expected<Function *> compileFunc(const FunctionDecl *FuncDecl); protected: ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {} @@ -83,7 +83,7 @@ private: /// Offset of the next local variable. unsigned NextLocalOffset = 0; /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; + std::optional<SourceLocation> BailLocation; /// Label information for linker. llvm::DenseMap<LabelTy, unsigned> LabelOffsets; /// Location of label relocations. diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 9b729e347a24..615dbdefefbe 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -9,6 +9,7 @@ #include "ByteCodeExprGen.h" #include "ByteCodeEmitter.h" #include "ByteCodeGenError.h" +#include "ByteCodeStmtGen.h" #include "Context.h" #include "Function.h" #include "PrimType.h" @@ -19,8 +20,6 @@ using namespace clang; using namespace clang::interp; using APSInt = llvm::APSInt; -template <typename T> using Expected = llvm::Expected<T>; -template <typename T> using Optional = llvm::Optional<T>; namespace clang { namespace interp { @@ -42,45 +41,19 @@ private: /// Scope used to handle initialization methods. template <class Emitter> class OptionScope { public: - using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef; - using ChainedInitFnRef = std::function<bool(InitFnRef)>; - /// Root constructor, compiling or discarding primitives. OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult) { Ctx->DiscardResult = NewDiscardResult; - Ctx->InitFn = llvm::Optional<InitFnRef>{}; - } - - /// Root constructor, setting up compilation state. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, InitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - Ctx->DiscardResult = true; - Ctx->InitFn = NewInitFn; } - /// Extends the chain of initialisation pointers. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, ChainedInitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - assert(OldInitFn && "missing initializer"); - Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); }; - } - - ~OptionScope() { - Ctx->DiscardResult = OldDiscardResult; - Ctx->InitFn = std::move(OldInitFn); - } + ~OptionScope() { Ctx->DiscardResult = OldDiscardResult; } private: /// Parent context. ByteCodeExprGen<Emitter> *Ctx; /// Old discard flag to restore. bool OldDiscardResult; - /// Old pointer emitter to restore. - llvm::Optional<InitFnRef> OldInitFn; }; } // namespace interp @@ -106,6 +79,22 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { }); } + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + if (!this->visit(SubExpr)) + return false; + + const CXXRecordDecl *FromDecl = getRecordDecl(SubExpr); + assert(FromDecl); + const CXXRecordDecl *ToDecl = getRecordDecl(CE); + assert(ToDecl); + const Record *R = getRecord(FromDecl); + const Record::Base *ToBase = R->getBase(ToDecl); + assert(ToBase); + + return this->emitGetPtrBase(ToBase->Offset, CE); + } + case CK_ArrayToPointerDecay: case CK_AtomicToNonAtomic: case CK_ConstructorConversion: @@ -113,16 +102,30 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { case CK_NonAtomicToAtomic: case CK_NoOp: case CK_UserDefinedConversion: - return this->Visit(SubExpr); + case CK_NullToPointer: + return this->visit(SubExpr); + + case CK_IntegralToBoolean: + case CK_IntegralCast: { + std::optional<PrimType> FromT = classify(SubExpr->getType()); + std::optional<PrimType> ToT = classify(CE->getType()); + if (!FromT || !ToT) + return false; + + if (!this->visit(SubExpr)) + return false; + + // TODO: Emit this only if FromT != ToT. + return this->emitCast(*FromT, *ToT, CE); + } case CK_ToVoid: return discard(SubExpr); - default: { - // TODO: implement other casts. - return this->bail(CE); - } + default: + assert(false && "Cast not implemented"); } + llvm_unreachable("Unhandled clang::CastKind enum"); } template <class Emitter> @@ -130,16 +133,12 @@ bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) { if (DiscardResult) return true; - auto Val = LE->getValue(); - QualType LitTy = LE->getType(); - if (Optional<PrimType> T = classify(LitTy)) - return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE); - return this->bail(LE); + return this->emitConst(LE->getValue(), LE); } template <class Emitter> bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) { - return this->Visit(PE->getSubExpr()); + return this->visit(PE->getSubExpr()); } template <class Emitter> @@ -152,7 +151,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { case BO_Comma: if (!discard(LHS)) return false; - if (!this->Visit(RHS)) + if (!this->visit(RHS)) return false; return true; default: @@ -160,53 +159,398 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { } // Typecheck the args. - Optional<PrimType> LT = classify(LHS->getType()); - Optional<PrimType> RT = classify(RHS->getType()); - if (!LT || !RT) { + std::optional<PrimType> LT = classify(LHS->getType()); + std::optional<PrimType> RT = classify(RHS->getType()); + std::optional<PrimType> T = classify(BO->getType()); + if (!LT || !RT || !T) { return this->bail(BO); } - if (Optional<PrimType> T = classify(BO->getType())) { - if (!visit(LHS)) + auto Discard = [this, T, BO](bool Result) { + if (!Result) return false; - if (!visit(RHS)) + return DiscardResult ? this->emitPop(*T, BO) : true; + }; + + // Pointer arithmetic special case. + if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) { + if (*T == PT_Ptr || (*LT == PT_Ptr && *RT == PT_Ptr)) + return this->VisitPointerArithBinOp(BO); + } + + if (!visit(LHS) || !visit(RHS)) + return false; + + switch (BO->getOpcode()) { + case BO_EQ: + return Discard(this->emitEQ(*LT, BO)); + case BO_NE: + return Discard(this->emitNE(*LT, BO)); + case BO_LT: + return Discard(this->emitLT(*LT, BO)); + case BO_LE: + return Discard(this->emitLE(*LT, BO)); + case BO_GT: + return Discard(this->emitGT(*LT, BO)); + case BO_GE: + return Discard(this->emitGE(*LT, BO)); + case BO_Sub: + return Discard(this->emitSub(*T, BO)); + case BO_Add: + return Discard(this->emitAdd(*T, BO)); + case BO_Mul: + return Discard(this->emitMul(*T, BO)); + case BO_Rem: + return Discard(this->emitRem(*T, BO)); + case BO_Div: + return Discard(this->emitDiv(*T, BO)); + case BO_Assign: + if (DiscardResult) + return this->emitStorePop(*T, BO); + return this->emitStore(*T, BO); + case BO_And: + return Discard(this->emitBitAnd(*T, BO)); + case BO_Or: + return Discard(this->emitBitOr(*T, BO)); + case BO_Shl: + return Discard(this->emitShl(*LT, *RT, BO)); + case BO_Shr: + return Discard(this->emitShr(*LT, *RT, BO)); + case BO_Xor: + return Discard(this->emitBitXor(*T, BO)); + case BO_LAnd: + case BO_LOr: + default: + return this->bail(BO); + } + + llvm_unreachable("Unhandled binary op"); +} + +/// Perform addition/subtraction of a pointer and an integer or +/// subtraction of two pointers. +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) { + BinaryOperatorKind Op = E->getOpcode(); + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + + if ((Op != BO_Add && Op != BO_Sub) || + (!LHS->getType()->isPointerType() && !RHS->getType()->isPointerType())) + return false; + + std::optional<PrimType> LT = classify(LHS); + std::optional<PrimType> RT = classify(RHS); + + if (!LT || !RT) + return false; + + if (LHS->getType()->isPointerType() && RHS->getType()->isPointerType()) { + if (Op != BO_Sub) + return false; + + assert(E->getType()->isIntegerType()); + if (!visit(RHS) || !visit(LHS)) + return false; + + return this->emitSubPtr(classifyPrim(E->getType()), E); + } + + PrimType OffsetType; + if (LHS->getType()->isIntegerType()) { + if (!visit(RHS) || !visit(LHS)) + return false; + OffsetType = *LT; + } else if (RHS->getType()->isIntegerType()) { + if (!visit(LHS) || !visit(RHS)) + return false; + OffsetType = *RT; + } else { + return false; + } + + if (Op == BO_Add) + return this->emitAddOffset(OffsetType, E); + else if (Op == BO_Sub) + return this->emitSubOffset(OffsetType, E); + + return this->bail(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { + if (std::optional<PrimType> T = classify(E)) + return this->emitZero(*T, E); + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr( + const ArraySubscriptExpr *E) { + const Expr *Base = E->getBase(); + const Expr *Index = E->getIdx(); + PrimType IndexT = classifyPrim(Index->getType()); + + // Take pointer of LHS, add offset from RHS, narrow result. + // What's left on the stack after this is a pointer. + if (!this->visit(Base)) + return false; + + if (!this->visit(Index)) + return false; + + if (!this->emitAddOffset(IndexT, E)) + return false; + + if (!this->emitNarrowPtr(E)) + return false; + + if (DiscardResult) + return this->emitPopPtr(E); + + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) { + for (const Expr *Init : E->inits()) { + if (!this->visit(Init)) return false; + } + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr( + const SubstNonTypeTemplateParmExpr *E) { + return this->visit(E->getReplacement()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) { + // TODO: Check if the ConstantExpr already has a value set and if so, + // use that instead of evaluating it again. + return this->visit(E->getSubExpr()); +} + +static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx, + UnaryExprOrTypeTrait Kind) { + bool AlignOfReturnsPreferred = + ASTCtx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7; - auto Discard = [this, T, BO](bool Result) { - if (!Result) + // C++ [expr.alignof]p3: + // When alignof is applied to a reference type, the result is the + // alignment of the referenced type. + if (const auto *Ref = T->getAs<ReferenceType>()) + T = Ref->getPointeeType(); + + // __alignof is defined to return the preferred alignment. + // Before 8, clang returned the preferred alignment for alignof and + // _Alignof as well. + if (Kind == UETT_PreferredAlignOf || AlignOfReturnsPreferred) + return ASTCtx.toCharUnitsFromBits(ASTCtx.getPreferredTypeAlign(T)); + + return ASTCtx.getTypeAlignInChars(T); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr( + const UnaryExprOrTypeTraitExpr *E) { + UnaryExprOrTypeTrait Kind = E->getKind(); + ASTContext &ASTCtx = Ctx.getASTContext(); + + if (Kind == UETT_SizeOf) { + QualType ArgType = E->getTypeOfArgument(); + CharUnits Size; + if (ArgType->isVoidType() || ArgType->isFunctionType()) + Size = CharUnits::One(); + else { + if (ArgType->isDependentType() || !ArgType->isConstantSizeType()) return false; - return DiscardResult ? this->emitPop(*T, BO) : true; - }; - - switch (BO->getOpcode()) { - case BO_EQ: - return Discard(this->emitEQ(*LT, BO)); - case BO_NE: - return Discard(this->emitNE(*LT, BO)); - case BO_LT: - return Discard(this->emitLT(*LT, BO)); - case BO_LE: - return Discard(this->emitLE(*LT, BO)); - case BO_GT: - return Discard(this->emitGT(*LT, BO)); - case BO_GE: - return Discard(this->emitGE(*LT, BO)); - case BO_Sub: - return Discard(this->emitSub(*T, BO)); - case BO_Add: - return Discard(this->emitAdd(*T, BO)); - case BO_Mul: - return Discard(this->emitMul(*T, BO)); - default: - return this->bail(BO); + + Size = ASTCtx.getTypeSizeInChars(ArgType); } + + return this->emitConst(Size.getQuantity(), E); + } + + if (Kind == UETT_AlignOf || Kind == UETT_PreferredAlignOf) { + CharUnits Size; + + if (E->isArgumentType()) { + QualType ArgType = E->getTypeOfArgument(); + + Size = AlignOfType(ArgType, ASTCtx, Kind); + } else { + // Argument is an expression, not a type. + const Expr *Arg = E->getArgumentExpr()->IgnoreParens(); + + // The kinds of expressions that we have special-case logic here for + // should be kept up to date with the special checks for those + // expressions in Sema. + + // alignof decl is always accepted, even if it doesn't make sense: we + // default to 1 in those cases. + if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) + Size = ASTCtx.getDeclAlign(DRE->getDecl(), + /*RefAsPointee*/ true); + else if (const auto *ME = dyn_cast<MemberExpr>(Arg)) + Size = ASTCtx.getDeclAlign(ME->getMemberDecl(), + /*RefAsPointee*/ true); + else + Size = AlignOfType(Arg->getType(), ASTCtx, Kind); + } + + return this->emitConst(Size.getQuantity(), E); } - return this->bail(BO); + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) { + if (DiscardResult) + return true; + + // 'Base.Member' + const Expr *Base = E->getBase(); + const ValueDecl *Member = E->getMemberDecl(); + + if (!this->visit(Base)) + return false; + + // Base above gives us a pointer on the stack. + // TODO: Implement non-FieldDecl members. + if (const auto *FD = dyn_cast<FieldDecl>(Member)) { + const RecordDecl *RD = FD->getParent(); + const Record *R = getRecord(RD); + const Record::Field *F = R->getField(FD); + // Leave a pointer to the field on the stack. + if (F->Decl->getType()->isReferenceType()) + return this->emitGetFieldPop(PT_Ptr, F->Offset, E); + return this->emitGetPtrField(F->Offset, E); + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitArrayInitIndexExpr( + const ArrayInitIndexExpr *E) { + // ArrayIndex might not be set if a ArrayInitIndexExpr is being evaluated + // stand-alone, e.g. via EvaluateAsInt(). + if (!ArrayIndex) + return false; + return this->emitConst(*ArrayIndex, E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + return this->visit(E->getSourceExpr()); } template <class Emitter> -bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { +bool ByteCodeExprGen<Emitter>::VisitAbstractConditionalOperator( + const AbstractConditionalOperator *E) { + const Expr *Condition = E->getCond(); + const Expr *TrueExpr = E->getTrueExpr(); + const Expr *FalseExpr = E->getFalseExpr(); + + LabelTy LabelEnd = this->getLabel(); // Label after the operator. + LabelTy LabelFalse = this->getLabel(); // Label for the false expr. + + if (!this->visit(Condition)) + return false; + if (!this->jumpFalse(LabelFalse)) + return false; + + if (!this->visit(TrueExpr)) + return false; + if (!this->jump(LabelEnd)) + return false; + + this->emitLabel(LabelFalse); + + if (!this->visit(FalseExpr)) + return false; + + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitStringLiteral(const StringLiteral *E) { + unsigned StringIndex = P.createGlobalString(E); + return this->emitGetPtrGlobal(StringIndex, E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCharacterLiteral( + const CharacterLiteral *E) { + return this->emitConst(E->getValue(), E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator( + const CompoundAssignOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + std::optional<PrimType> LT = classify(E->getLHS()->getType()); + std::optional<PrimType> RT = classify(E->getRHS()->getType()); + + if (!LT || !RT) + return false; + + assert(!E->getType()->isPointerType() && + "Support pointer arithmethic in compound assignment operators"); + + // Get LHS pointer, load its value and get RHS value. + if (!visit(LHS)) + return false; + if (!this->emitLoad(*LT, E)) + return false; + if (!visit(RHS)) + return false; + + // Perform operation. + switch (E->getOpcode()) { + case BO_AddAssign: + if (!this->emitAdd(*LT, E)) + return false; + break; + case BO_SubAssign: + if (!this->emitSub(*LT, E)) + return false; + break; + + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_ShlAssign: + if (!this->emitShl(*LT, *RT, E)) + return false; + break; + case BO_ShrAssign: + if (!this->emitShr(*LT, *RT, E)) + return false; + break; + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + default: + llvm_unreachable("Unimplemented compound assign operator"); + } + + // And store the result in LHS. + if (DiscardResult) + return this->emitStorePop(*LT, E); + return this->emitStore(*LT, E); +} + +template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); } @@ -219,7 +563,7 @@ bool ByteCodeExprGen<Emitter>::visit(const Expr *E) { template <class Emitter> bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) { - if (Optional<PrimType> T = classify(E->getType())) { + if (std::optional<PrimType> T = classify(E->getType())) { return visit(E); } else { return this->bail(E); @@ -257,7 +601,7 @@ template <class Emitter> bool ByteCodeExprGen<Emitter>::dereference( const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, llvm::function_ref<bool(PrimType)> Indirect) { - if (Optional<PrimType> T = classify(LV->getType())) { + if (std::optional<PrimType> T = classify(LV->getType())) { if (!LV->refersToBitField()) { // Only primitive, non bit-field types can be dereferenced directly. if (auto *DE = dyn_cast<DeclRefExpr>(LV)) { @@ -350,7 +694,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( return false; return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); } - } else if (auto Idx = getGlobalIdx(VD)) { + } else if (auto Idx = P.getGlobal(VD)) { switch (AK) { case DerefKind::Read: if (!this->emitGetGlobal(T, *Idx, LV)) @@ -382,7 +726,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { QualType VT = VD->getType(); if (VT.isConstQualified() && VT->isFundamentalType()) - return this->Visit(VD->getInit()); + return this->visit(VD->getInit()); } } @@ -391,27 +735,27 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( } template <class Emitter> -bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, - const APInt &Value, const Expr *E) { - switch (T) { +template <typename T> +bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) { + switch (classifyPrim(E->getType())) { case PT_Sint8: - return this->emitConstSint8(Value.getSExtValue(), E); + return this->emitConstSint8(Value, E); case PT_Uint8: - return this->emitConstUint8(Value.getZExtValue(), E); + return this->emitConstUint8(Value, E); case PT_Sint16: - return this->emitConstSint16(Value.getSExtValue(), E); + return this->emitConstSint16(Value, E); case PT_Uint16: - return this->emitConstUint16(Value.getZExtValue(), E); + return this->emitConstUint16(Value, E); case PT_Sint32: - return this->emitConstSint32(Value.getSExtValue(), E); + return this->emitConstSint32(Value, E); case PT_Uint32: - return this->emitConstUint32(Value.getZExtValue(), E); + return this->emitConstUint32(Value, E); case PT_Sint64: - return this->emitConstSint64(Value.getSExtValue(), E); + return this->emitConstSint64(Value, E); case PT_Uint64: - return this->emitConstUint64(Value.getZExtValue(), E); + return this->emitConstUint64(Value, E); case PT_Bool: - return this->emitConstBool(Value.getBoolValue(), E); + return this->emitConstBool(Value, E); case PT_Ptr: llvm_unreachable("Invalid integral type"); break; @@ -420,11 +764,29 @@ bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, } template <class Emitter> +bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, const Expr *E) { + if (Value.isSigned()) + return this->emitConst(Value.getSExtValue(), E); + return this->emitConst(Value.getZExtValue(), E); +} + +template <class Emitter> unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty, bool IsConst, bool IsExtended) { - Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>()); + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + + // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g. + // (int){12} in C. Consider using Expr::isTemporaryObject() instead + // or isa<MaterializeTemporaryExpr>(). + Descriptor *D = P.createDescriptor(Src, Ty, Descriptor::InlineDescMD, IsConst, + Src.is<const Expr *>()); Scope::Local Local = this->createLocal(D); if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) Locals.insert({VD, Local}); @@ -433,23 +795,34 @@ unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src, } template <class Emitter> -llvm::Optional<unsigned> +std::optional<unsigned> ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { - QualType Ty; + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + QualType Ty; const ValueDecl *Key = nullptr; + const Expr *Init = nullptr; bool IsTemporary = false; - if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) { + if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { Key = VD; Ty = VD->getType(); + + if (const auto *VarD = dyn_cast<VarDecl>(VD)) + Init = VarD->getInit(); } if (auto *E = Src.dyn_cast<const Expr *>()) { IsTemporary = true; Ty = E->getType(); } - Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(), - Ty.isConstQualified(), IsTemporary); + Descriptor *D = P.createDescriptor( + Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(), + IsTemporary, /*IsMutable=*/false, Init); if (!D) return {}; @@ -460,38 +833,230 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { return Local.Offset; } +// NB: When calling this function, we have a pointer to the +// array-to-initialize on the stack. template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitInitializer( - const Expr *Init, InitFnRef InitFn) { - OptionScope<Emitter> Scope(this, InitFn); - return this->Visit(Init); +bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) { + assert(Initializer->getType()->isArrayType()); + + // TODO: Fillers? + if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) { + unsigned ElementIndex = 0; + for (const Expr *Init : InitList->inits()) { + if (std::optional<PrimType> T = classify(Init->getType())) { + // Visit the primitive element like normal. + if (!this->emitDupPtr(Init)) + return false; + if (!this->visit(Init)) + return false; + if (!this->emitInitElem(*T, ElementIndex, Init)) + return false; + } else { + // Advance the pointer currently on the stack to the given + // dimension and narrow(). + if (!this->emitDupPtr(Init)) + return false; + if (!this->emitConstUint32(ElementIndex, Init)) + return false; + if (!this->emitAddOffsetUint32(Init)) + return false; + if (!this->emitNarrowPtr(Init)) + return false; + + if (!visitInitializer(Init)) + return false; + } + if (!this->emitPopPtr(Init)) + return false; + + ++ElementIndex; + } + return true; + } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) { + return this->visitInitializer(DIE->getExpr()); + } else if (const auto *AILE = dyn_cast<ArrayInitLoopExpr>(Initializer)) { + // TODO: This compiles to quite a lot of bytecode if the array is larger. + // Investigate compiling this to a loop, or at least try to use + // the AILE's Common expr. + const Expr *SubExpr = AILE->getSubExpr(); + size_t Size = AILE->getArraySize().getZExtValue(); + std::optional<PrimType> ElemT = classify(SubExpr->getType()); + + // So, every iteration, we execute an assignment here + // where the LHS is on the stack (the target array) + // and the RHS is our SubExpr. + for (size_t I = 0; I != Size; ++I) { + ArrayIndexScope<Emitter> IndexScope(this, I); + + if (!this->emitDupPtr(SubExpr)) // LHS + return false; + + if (ElemT) { + if (!this->visit(SubExpr)) + return false; + if (!this->emitInitElem(*ElemT, I, Initializer)) + return false; + } else { + // Narrow to our array element and recurse into visitInitializer() + if (!this->emitConstUint64(I, SubExpr)) + return false; + + if (!this->emitAddOffsetUint64(SubExpr)) + return false; + + if (!this->emitNarrowPtr(SubExpr)) + return false; + + if (!visitInitializer(SubExpr)) + return false; + } + + if (!this->emitPopPtr(Initializer)) + return false; + } + return true; + } else if (const auto *IVIE = dyn_cast<ImplicitValueInitExpr>(Initializer)) { + const ArrayType *AT = IVIE->getType()->getAsArrayTypeUnsafe(); + assert(AT); + const auto *CAT = cast<ConstantArrayType>(AT); + size_t NumElems = CAT->getSize().getZExtValue(); + + if (std::optional<PrimType> ElemT = classify(CAT->getElementType())) { + // TODO(perf): For int and bool types, we can probably just skip this + // since we memset our Block*s to 0 and so we have the desired value + // without this. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->emitZero(*ElemT, Initializer)) + return false; + if (!this->emitInitElem(*ElemT, I, Initializer)) + return false; + } + } else { + assert(false && "default initializer for non-primitive type"); + } + + return true; + } else if (const auto *Ctor = dyn_cast<CXXConstructExpr>(Initializer)) { + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(Ctor->getType()); + assert(CAT); + size_t NumElems = CAT->getSize().getZExtValue(); + const Function *Func = getFunction(Ctor->getConstructor()); + if (!Func || !Func->isConstexpr()) + return false; + + // FIXME(perf): We're calling the constructor once per array element here, + // in the old intepreter we had a special-case for trivial constructors. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->emitDupPtr(Initializer)) + return false; + if (!this->emitConstUint64(I, Initializer)) + return false; + if (!this->emitAddOffsetUint64(Initializer)) + return false; + if (!this->emitNarrowPtr(Initializer)) + return false; + + // Constructor arguments. + for (const auto *Arg : Ctor->arguments()) { + if (!this->visit(Arg)) + return false; + } + + if (!this->emitCall(Func, Initializer)) + return false; + } + return true; + } + + assert(false && "Unknown expression for array initialization"); + return false; } template <class Emitter> -bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) { - // Generate a pointer to the local, loading refs. - if (Optional<unsigned> Idx = getGlobalIdx(VD)) { - if (VD->getType()->isReferenceType()) - return this->emitGetGlobalPtr(*Idx, E); - else - return this->emitGetPtrGlobal(*Idx, E); +bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) { + Initializer = Initializer->IgnoreParenImpCasts(); + assert(Initializer->getType()->isRecordType()); + + if (const auto CtorExpr = dyn_cast<CXXConstructExpr>(Initializer)) { + const Function *Func = getFunction(CtorExpr->getConstructor()); + + if (!Func || !Func->isConstexpr()) + return false; + + // The This pointer is already on the stack because this is an initializer, + // but we need to dup() so the call() below has its own copy. + if (!this->emitDupPtr(Initializer)) + return false; + + // Constructor arguments. + for (const auto *Arg : CtorExpr->arguments()) { + if (!this->visit(Arg)) + return false; + } + + return this->emitCall(Func, Initializer); + } else if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) { + const Record *R = getRecord(InitList->getType()); + + unsigned InitIndex = 0; + for (const Expr *Init : InitList->inits()) { + const Record::Field *FieldToInit = R->getField(InitIndex); + + if (!this->emitDupPtr(Initializer)) + return false; + + if (std::optional<PrimType> T = classify(Init)) { + if (!this->visit(Init)) + return false; + + if (!this->emitInitField(*T, FieldToInit->Offset, Initializer)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; + } else { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and recurse into visitInitializer(). + if (!this->emitGetPtrField(FieldToInit->Offset, Init)) + return false; + + if (!this->visitInitializer(Init)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; + } + ++InitIndex; + } + + return true; + } else if (const CallExpr *CE = dyn_cast<CallExpr>(Initializer)) { + // RVO functions expect a pointer to initialize on the stack. + // Dup our existing pointer so it has its own copy to use. + if (!this->emitDupPtr(Initializer)) + return false; + + return this->VisitCallExpr(CE); + } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) { + return this->visitInitializer(DIE->getExpr()); } - return this->bail(VD); + + return false; } template <class Emitter> -llvm::Optional<unsigned> -ByteCodeExprGen<Emitter>::getGlobalIdx(const VarDecl *VD) { - if (VD->isConstexpr()) { - // Constexpr decl - it must have already been defined. - return P.getGlobal(VD); - } - if (!VD->hasLocalStorage()) { - // Not constexpr, but a global var - can have pointer taken. - Program::DeclScope Scope(P, VD); - return P.getOrCreateGlobal(VD); - } - return {}; +bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) { + QualType InitializerType = Initializer->getType(); + + if (InitializerType->isArrayType()) + return visitArrayInitializer(Initializer); + + if (InitializerType->isRecordType()) + return visitRecordInitializer(Initializer); + + // Otherwise, visit the expression like normal. + return this->visit(Initializer); } template <class Emitter> @@ -516,52 +1081,339 @@ Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) { } template <class Emitter> +const Function *ByteCodeExprGen<Emitter>::getFunction(const FunctionDecl *FD) { + assert(FD); + const Function *Func = P.getFunction(FD); + bool IsBeingCompiled = Func && !Func->isFullyCompiled(); + bool WasNotDefined = Func && !Func->hasBody(); + + if (IsBeingCompiled) + return Func; + + if (!Func || WasNotDefined) { + if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FD)) + Func = *R; + else { + llvm::consumeError(R.takeError()); + return nullptr; + } + } + + return Func; +} + +template <class Emitter> bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) { ExprScope<Emitter> RootScope(this); if (!visit(Exp)) return false; - if (Optional<PrimType> T = classify(Exp)) + if (std::optional<PrimType> T = classify(Exp)) return this->emitRet(*T, Exp); else return this->emitRetValue(Exp); } +/// Toplevel visitDecl(). +/// We get here from evaluateAsInitializer(). +/// We need to evaluate the initializer and return its value. template <class Emitter> bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) { + std::optional<PrimType> VarT = classify(VD->getType()); + + // Create and initialize the variable. + if (!this->visitVarDecl(VD)) + return false; + + // Get a pointer to the variable + if (shouldBeGloballyIndexed(VD)) { + auto GlobalIndex = P.getGlobal(VD); + assert(GlobalIndex); // visitVarDecl() didn't return false. + if (!this->emitGetPtrGlobal(*GlobalIndex, VD)) + return false; + } else { + auto Local = Locals.find(VD); + assert(Local != Locals.end()); // Same here. + if (!this->emitGetPtrLocal(Local->second.Offset, VD)) + return false; + } + + // Return the value + if (VarT) { + if (!this->emitLoadPop(*VarT, VD)) + return false; + + return this->emitRet(*VarT, VD); + } + + return this->emitRetValue(VD); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) { const Expr *Init = VD->getInit(); + std::optional<PrimType> VarT = classify(VD->getType()); + + if (shouldBeGloballyIndexed(VD)) { + std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(VD, Init); + + if (!GlobalIndex) + return this->bail(VD); + + assert(Init); + { + DeclScope<Emitter> LocalScope(this, VD); - if (Optional<unsigned> I = P.createGlobal(VD)) { - if (Optional<PrimType> T = classify(VD->getType())) { - { - // Primitive declarations - compute the value and set it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visit(Init)) + if (VarT) { + if (!this->visit(Init)) return false; + return this->emitInitGlobal(*VarT, *GlobalIndex, VD); } + return this->visitGlobalInitializer(Init, *GlobalIndex); + } + } else { + VariableScope<Emitter> LocalScope(this); + if (VarT) { + unsigned Offset = this->allocateLocalPrimitive( + VD, *VarT, VD->getType().isConstQualified()); + if (Init) { + // Compile the initializer in its own scope. + ExprScope<Emitter> Scope(this); + if (!this->visit(Init)) + return false; - // If the declaration is global, save the value for later use. - if (!this->emitDup(*T, VD)) - return false; - if (!this->emitInitGlobal(*T, *I, VD)) - return false; - return this->emitRet(*T, VD); + return this->emitSetLocal(*VarT, Offset, VD); + } } else { - { - // Composite declarations - allocate storage and initialize it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visitGlobalInitializer(Init, *I)) + if (std::optional<unsigned> Offset = this->allocateLocal(VD)) { + if (Init) + return this->visitLocalInitializer(Init, *Offset); + } + } + return true; + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) { + assert(!E->getBuiltinCallee() && "Builtin functions aren't supported yet"); + + const Decl *Callee = E->getCalleeDecl(); + if (const auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(Callee)) { + const Function *Func = getFunction(FuncDecl); + if (!Func) + return false; + // If the function is being compiled right now, this is a recursive call. + // In that case, the function can't be valid yet, even though it will be + // later. + // If the function is already fully compiled but not constexpr, it was + // found to be faulty earlier on, so bail out. + if (Func->isFullyCompiled() && !Func->isConstexpr()) + return false; + + QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); + std::optional<PrimType> T = classify(ReturnType); + + if (Func->hasRVO() && DiscardResult) { + // If we need to discard the return value but the function returns its + // value via an RVO pointer, we need to create one such pointer just + // for this call. + if (std::optional<unsigned> LocalIndex = allocateLocal(E)) { + if (!this->emitGetPtrLocal(*LocalIndex, E)) return false; } + } - // Return a pointer to the global. - if (!this->emitGetPtrGlobal(*I, VD)) + // Put arguments on the stack. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) return false; - return this->emitRetValue(VD); } + + // In any case call the function. The return value will end up on the stack and + // if the function has RVO, we already have the pointer on the stack to write + // the result into. + if (!this->emitCall(Func, E)) + return false; + + if (DiscardResult && !ReturnType->isVoidType() && T) + return this->emitPop(*T, E); + + return true; + } else { + assert(false && "We don't support non-FunctionDecl callees right now."); } - return this->bail(VD); + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXMemberCallExpr( + const CXXMemberCallExpr *E) { + // Get a This pointer on the stack. + if (!this->visit(E->getImplicitObjectArgument())) + return false; + + return VisitCallExpr(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr( + const CXXDefaultInitExpr *E) { + return this->visit(E->getExpr()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXDefaultArgExpr( + const CXXDefaultArgExpr *E) { + return this->visit(E->getExpr()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXBoolLiteralExpr( + const CXXBoolLiteralExpr *E) { + if (DiscardResult) + return true; + + return this->emitConstBool(E->getValue(), E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr( + const CXXNullPtrLiteralExpr *E) { + if (DiscardResult) + return true; + + return this->emitNullPtr(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) { + if (DiscardResult) + return true; + return this->emitThis(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { + const Expr *SubExpr = E->getSubExpr(); + std::optional<PrimType> T = classify(SubExpr->getType()); + + // TODO: Support pointers for inc/dec operators. + switch (E->getOpcode()) { + case UO_PostInc: { // x++ + if (!this->visit(SubExpr)) + return false; + + return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E); + } + case UO_PostDec: { // x-- + if (!this->visit(SubExpr)) + return false; + + return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E); + } + case UO_PreInc: { // ++x + if (!this->visit(SubExpr)) + return false; + + // Post-inc and pre-inc are the same if the value is to be discarded. + if (DiscardResult) + return this->emitIncPop(*T, E); + + this->emitLoad(*T, E); + this->emitConst(1, E); + this->emitAdd(*T, E); + return this->emitStore(*T, E); + } + case UO_PreDec: { // --x + if (!this->visit(SubExpr)) + return false; + + // Post-dec and pre-dec are the same if the value is to be discarded. + if (DiscardResult) + return this->emitDecPop(*T, E); + + this->emitLoad(*T, E); + this->emitConst(1, E); + this->emitSub(*T, E); + return this->emitStore(*T, E); + } + case UO_LNot: // !x + if (!this->visit(SubExpr)) + return false; + // The Inv doesn't change anything, so skip it if we don't need the result. + return DiscardResult ? this->emitPop(*T, E) : this->emitInvBool(E); + case UO_Minus: // -x + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : this->emitNeg(*T, E); + case UO_Plus: // +x + if (!this->visit(SubExpr)) // noop + return false; + return DiscardResult ? this->emitPop(*T, E) : true; + case UO_AddrOf: // &x + // We should already have a pointer when we get here. + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : true; + case UO_Deref: // *x + return dereference( + SubExpr, DerefKind::Read, + [](PrimType) { + llvm_unreachable("Dereferencing requires a pointer"); + return false; + }, + [this, E](PrimType T) { + return DiscardResult ? this->emitPop(T, E) : true; + }); + case UO_Not: // ~x + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : this->emitComp(*T, E); + case UO_Real: // __real x + case UO_Imag: // __imag x + case UO_Extension: + case UO_Coawait: + assert(false && "Unhandled opcode"); + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) { + const auto *Decl = E->getDecl(); + // References are implemented via pointers, so when we see a DeclRefExpr + // pointing to a reference, we need to get its value directly (i.e. the + // pointer to the actual value) instead of a pointer to the pointer to the + // value. + bool IsReference = Decl->getType()->isReferenceType(); + + if (auto It = Locals.find(Decl); It != Locals.end()) { + const unsigned Offset = It->second.Offset; + + if (IsReference) + return this->emitGetLocal(PT_Ptr, Offset, E); + return this->emitGetPtrLocal(Offset, E); + } else if (auto GlobalIndex = P.getGlobal(Decl)) { + if (IsReference) + return this->emitGetGlobal(PT_Ptr, *GlobalIndex, E); + + return this->emitGetPtrGlobal(*GlobalIndex, E); + } else if (const auto *PVD = dyn_cast<ParmVarDecl>(Decl)) { + if (auto It = this->Params.find(PVD); It != this->Params.end()) { + if (IsReference) + return this->emitGetParam(PT_Ptr, It->second, E); + return this->emitGetPtrParam(It->second, E); + } + } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(Decl)) { + return this->emitConst(ECD->getInitVal(), E); + } + + return false; } template <class Emitter> diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 82aa413dabbc..c7fcc59e5a60 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -22,7 +22,6 @@ #include "clang/AST/Expr.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/TargetInfo.h" -#include "llvm/ADT/Optional.h" namespace clang { class QualType; @@ -34,25 +33,17 @@ template <class Emitter> class RecordScope; template <class Emitter> class VariableScope; template <class Emitter> class DeclScope; template <class Emitter> class OptionScope; +template <class Emitter> class ArrayIndexScope; /// Compilation context for expressions. template <class Emitter> class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, public Emitter { protected: - // Emitters for opcodes of various arities. - using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); - using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); - using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, - const SourceInfo &); - // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - // Reference to a function generating the pointer of an initialized object.s - using InitFnRef = std::function<bool()>; - /// Current compilation context. Context &Ctx; /// Program to link to. @@ -64,11 +55,34 @@ public: ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} - // Expression visitors - result returned on stack. + // Expression visitors - result returned on interp stack. bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); + bool VisitPointerArithBinOp(const BinaryOperator *E); + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); + bool VisitCallExpr(const CallExpr *E); + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E); + bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); + bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); + bool VisitCXXThisExpr(const CXXThisExpr *E); + bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitDeclRefExpr(const DeclRefExpr *E); + bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E); + bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E); + bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E); + bool VisitInitListExpr(const InitListExpr *E); + bool VisitConstantExpr(const ConstantExpr *E); + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); + bool VisitMemberExpr(const MemberExpr *E); + bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E); + bool VisitOpaqueValueExpr(const OpaqueValueExpr *E); + bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E); + bool VisitStringLiteral(const StringLiteral *E); + bool VisitCharacterLiteral(const CharacterLiteral *E); + bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); protected: bool visitExpr(const Expr *E) override; @@ -85,31 +99,18 @@ protected: Record *getRecord(QualType Ty); Record *getRecord(const RecordDecl *RD); - /// Returns the size int bits of an integer. - unsigned getIntWidth(QualType Ty) { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getIntWidth(Ty); - } - - /// Returns the value of CHAR_BIT. - unsigned getCharBit() const { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getTargetInfo().getCharWidth(); - } + // Returns a function for the given FunctionDecl. + // If the function does not exist yet, it is compiled. + const Function *getFunction(const FunctionDecl *FD); /// Classifies a type. - llvm::Optional<PrimType> classify(const Expr *E) const { + std::optional<PrimType> classify(const Expr *E) const { return E->isGLValue() ? PT_Ptr : classify(E->getType()); } - llvm::Optional<PrimType> classify(QualType Ty) const { + std::optional<PrimType> classify(QualType Ty) const { return Ctx.classify(Ty); } - /// Checks if a pointer needs adjustment. - bool needsAdjust(QualType Ty) const { - return true; - } - /// Classifies a known primitive type PrimType classifyPrim(QualType Ty) const { if (auto T = classify(Ty)) { @@ -122,29 +123,49 @@ protected: bool discard(const Expr *E); /// Evaluates an expression and places result on stack. bool visit(const Expr *E); - /// Compiles an initializer for a local. - bool visitInitializer(const Expr *E, InitFnRef GenPtr); + /// Compiles an initializer. + bool visitInitializer(const Expr *E); + /// Compiles an array initializer. + bool visitArrayInitializer(const Expr *Initializer); + /// Compiles a record initializer. + bool visitRecordInitializer(const Expr *Initializer); + /// Creates and initializes a variable from the given decl. + bool visitVarDecl(const VarDecl *VD); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); /// Visits an initializer for a local. bool visitLocalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrLocal(I, Init); - }); + if (!this->emitGetPtrLocal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits an initializer for a global. bool visitGlobalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrGlobal(I, Init); - }); + if (!this->emitGetPtrGlobal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits a delegated initializer. bool visitThisInitializer(const Expr *I) { - return visitInitializer(I, [this, I] { return this->emitThis(I); }); + if (!this->emitThis(I)) + return false; + + if (!visitInitializer(I)) + return false; + + return this->emitPopPtr(I); } /// Creates a local primitive value. @@ -152,8 +173,7 @@ protected: bool IsExtended = false); /// Allocates a space storing a local given its type. - llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl, - bool IsExtended = false); + std::optional<unsigned> allocateLocal(DeclTy &&Decl, bool IsExtended = false); private: friend class VariableScope<Emitter>; @@ -161,6 +181,7 @@ private: friend class RecordScope<Emitter>; friend class DeclScope<Emitter>; friend class OptionScope<Emitter>; + friend class ArrayIndexScope<Emitter>; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, const Expr *E); @@ -188,28 +209,28 @@ private: DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, llvm::function_ref<bool(PrimType)> Indirect); - /// Emits an APInt constant. - bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value, - const Expr *E); + /// Emits an APSInt constant. + bool emitConst(const APSInt &Value, const Expr *E); + bool emitConst(const APInt &Value, const Expr *E) { + return emitConst(static_cast<APSInt>(Value), E); + } /// Emits an integer constant. - template <typename T> bool emitConst(const Expr *E, T Value) { - QualType Ty = E->getType(); - unsigned NumBits = getIntWidth(Ty); - APInt WrappedValue(NumBits, Value, std::is_signed<T>::value); - return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E); + template <typename T> bool emitConst(T Value, const Expr *E); + + /// Returns the CXXRecordDecl for the type of the given expression, + /// or nullptr if no such decl exists. + const CXXRecordDecl *getRecordDecl(const Expr *E) const { + QualType T = E->getType(); + if (const auto *RD = T->getPointeeCXXRecordDecl()) + return RD; + return T->getAsCXXRecordDecl(); } - /// Returns a pointer to a variable declaration. - bool getPtrVarDecl(const VarDecl *VD, const Expr *E); - - /// Returns the index of a global. - llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD); - - /// Emits the initialized pointer. - bool emitInitFn() { - assert(InitFn && "missing initializer"); - return (*InitFn)(); + /// Returns whether we should create a global variable for the + /// given VarDecl. + bool shouldBeGloballyIndexed(const VarDecl *VD) const { + return VD->hasGlobalStorage() || VD->isConstexpr(); } protected: @@ -222,14 +243,11 @@ protected: /// Current scope. VariableScope<Emitter> *VarScope = nullptr; - /// Current argument index. - llvm::Optional<uint64_t> ArrayIndex; + /// Current argument index. Needed to emit ArrayInitIndexExpr. + std::optional<uint64_t> ArrayIndex; /// Flag indicating if return value is to be discarded. bool DiscardResult = false; - - /// Expression being initialized. - llvm::Optional<InitFnRef> InitFn = {}; }; extern template class ByteCodeExprGen<ByteCodeEmitter>; @@ -238,6 +256,11 @@ extern template class ByteCodeExprGen<EvalEmitter>; /// Scope chain managing the variable lifetimes. template <class Emitter> class VariableScope { public: + VariableScope(ByteCodeExprGen<Emitter> *Ctx) + : Ctx(Ctx), Parent(Ctx->VarScope) { + Ctx->VarScope = this; + } + virtual ~VariableScope() { Ctx->VarScope = this->Parent; } void add(const Scope::Local &Local, bool IsExtended) { @@ -262,11 +285,6 @@ public: VariableScope *getParent() { return Parent; } protected: - VariableScope(ByteCodeExprGen<Emitter> *Ctx) - : Ctx(Ctx), Parent(Ctx->VarScope) { - Ctx->VarScope = this; - } - /// ByteCodeExprGen instance. ByteCodeExprGen<Emitter> *Ctx; /// Link to the parent scope. @@ -300,7 +318,7 @@ public: protected: /// Index of the scope in the chain. - Optional<unsigned> Idx; + std::optional<unsigned> Idx; }; /// Scope for storage declared in a compound statement. @@ -320,10 +338,25 @@ public: ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {} void addExtended(const Scope::Local &Local) override { + assert(this->Parent); this->Parent->addLocal(Local); } }; +template <class Emitter> class ArrayIndexScope final { +public: + ArrayIndexScope(ByteCodeExprGen<Emitter> *Ctx, uint64_t Index) : Ctx(Ctx) { + OldArrayIndex = Ctx->ArrayIndex; + Ctx->ArrayIndex = Index; + } + + ~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; } + +private: + ByteCodeExprGen<Emitter> *Ctx; + std::optional<uint64_t> OldArrayIndex; +}; + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/Interp/ByteCodeGenError.h b/clang/lib/AST/Interp/ByteCodeGenError.h index a4fa4917705d..af464b5ed4ab 100644 --- a/clang/lib/AST/Interp/ByteCodeGenError.h +++ b/clang/lib/AST/Interp/ByteCodeGenError.h @@ -20,19 +20,19 @@ namespace interp { /// Error thrown by the compiler. struct ByteCodeGenError : public llvm::ErrorInfo<ByteCodeGenError> { public: - ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {} - ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {} - ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {} + ByteCodeGenError(SourceRange Range) : Range(Range) {} + ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getSourceRange()) {} + ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getSourceRange()) {} void log(raw_ostream &OS) const override { OS << "unimplemented feature"; } - const SourceLocation &getLoc() const { return Loc; } + const SourceRange &getRange() const { return Range; } static char ID; private: - // Start of the item where the error occurred. - SourceLocation Loc; + // Range of the item where the error occurred. + SourceRange Range; // Users are not expected to use error_code. std::error_code convertToErrorCode() const override { diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 90e84149b055..af97c57c98b7 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -94,11 +94,63 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { // Classify the return type. ReturnType = this->classify(F->getReturnType()); - // Set up fields and context if a constructor. - if (auto *MD = dyn_cast<CXXMethodDecl>(F)) - return this->bail(MD); + // Constructor. Set up field initializers. + if (const auto Ctor = dyn_cast<CXXConstructorDecl>(F)) { + const RecordDecl *RD = Ctor->getParent(); + const Record *R = this->getRecord(RD); + if (!R) + return false; + + for (const auto *Init : Ctor->inits()) { + const Expr *InitExpr = Init->getInit(); + if (const FieldDecl *Member = Init->getMember()) { + const Record::Field *F = R->getField(Member); + + if (std::optional<PrimType> T = this->classify(InitExpr)) { + if (!this->emitThis(InitExpr)) + return false; + + if (!this->visit(InitExpr)) + return false; + + if (!this->emitInitField(*T, F->Offset, InitExpr)) + return false; + + if (!this->emitPopPtr(InitExpr)) + return false; + } else { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and call visitInitialzer() for it. + if (!this->emitThis(InitExpr)) + return false; + + if (!this->emitGetPtrField(F->Offset, InitExpr)) + return false; + + if (!this->visitInitializer(InitExpr)) + return false; + + if (!this->emitPopPtr(InitExpr)) + return false; + } + } else if (const Type *Base = Init->getBaseClass()) { + // Base class initializer. + // Get This Base and call initializer on it. + auto *BaseDecl = Base->getAsCXXRecordDecl(); + assert(BaseDecl); + const Record::Base *B = R->getBase(BaseDecl); + assert(B); + if (!this->emitGetPtrThisBase(B->Offset, InitExpr)) + return false; + if (!this->visitInitializer(InitExpr)) + return false; + if (!this->emitPopPtr(InitExpr)) + return false; + } + } + } - if (auto *Body = F->getBody()) + if (const auto *Body = F->getBody()) if (!visitStmt(Body)) return false; @@ -120,6 +172,16 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) { return visitReturnStmt(cast<ReturnStmt>(S)); case Stmt::IfStmtClass: return visitIfStmt(cast<IfStmt>(S)); + case Stmt::WhileStmtClass: + return visitWhileStmt(cast<WhileStmt>(S)); + case Stmt::DoStmtClass: + return visitDoStmt(cast<DoStmt>(S)); + case Stmt::ForStmtClass: + return visitForStmt(cast<ForStmt>(S)); + case Stmt::BreakStmtClass: + return visitBreakStmt(cast<BreakStmt>(S)); + case Stmt::ContinueStmtClass: + return visitContinueStmt(cast<ContinueStmt>(S)); case Stmt::NullStmtClass: return true; default: { @@ -145,7 +207,7 @@ bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) { for (auto *D : DS->decls()) { // Variable declarator. if (auto *VD = dyn_cast<VarDecl>(D)) { - if (!visitVarDecl(VD)) + if (!this->visitVarDecl(VD)) return false; continue; } @@ -171,18 +233,21 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) { return this->emitRet(*ReturnType, RS); } else { // RVO - construct the value in the return location. - auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; - if (!this->visitInitializer(RE, ReturnLocation)) + if (!this->emitRVOPtr(RE)) return false; + if (!this->visitInitializer(RE)) + return false; + if (!this->emitPopPtr(RE)) + return false; + this->emitCleanup(); return this->emitRetVoid(RS); } - } else { - this->emitCleanup(); - if (!this->emitRetVoid(RS)) - return false; - return true; } + + // Void return. + this->emitCleanup(); + return this->emitRetVoid(RS); } template <class Emitter> @@ -231,33 +296,99 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) { } template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) { - auto DT = VD->getType(); +bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) { + const Expr *Cond = S->getCond(); + const Stmt *Body = S->getBody(); - if (!VD->hasLocalStorage()) { - // No code generation required. - return true; - } + LabelTy CondLabel = this->getLabel(); // Label before the condition. + LabelTy EndLabel = this->getLabel(); // Label after the loop. + LoopScope<Emitter> LS(this, EndLabel, CondLabel); - // Integers, pointers, primitives. - if (Optional<PrimType> T = this->classify(DT)) { - auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); - // Compile the initialiser in its own scope. - { - ExprScope<Emitter> Scope(this); - if (!this->visit(VD->getInit())) - return false; - } - // Set the value. - return this->emitSetLocal(*T, Off, VD); - } else { - // Composite types - allocate storage and initialize it. - if (auto Off = this->allocateLocal(VD)) { - return this->visitLocalInitializer(VD->getInit(), *Off); - } else { - return this->bail(VD); - } + this->emitLabel(CondLabel); + if (!this->visitBool(Cond)) + return false; + if (!this->jumpFalse(EndLabel)) + return false; + + if (!this->visitStmt(Body)) + return false; + if (!this->jump(CondLabel)) + return false; + + this->emitLabel(EndLabel); + + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) { + const Expr *Cond = S->getCond(); + const Stmt *Body = S->getBody(); + + LabelTy StartLabel = this->getLabel(); + LabelTy EndLabel = this->getLabel(); + LabelTy CondLabel = this->getLabel(); + LoopScope<Emitter> LS(this, EndLabel, CondLabel); + + this->emitLabel(StartLabel); + if (!this->visitStmt(Body)) + return false; + this->emitLabel(CondLabel); + if (!this->visitBool(Cond)) + return false; + if (!this->jumpTrue(StartLabel)) + return false; + this->emitLabel(EndLabel); + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitForStmt(const ForStmt *S) { + // for (Init; Cond; Inc) { Body } + const Stmt *Init = S->getInit(); + const Expr *Cond = S->getCond(); + const Expr *Inc = S->getInc(); + const Stmt *Body = S->getBody(); + + LabelTy EndLabel = this->getLabel(); + LabelTy CondLabel = this->getLabel(); + LabelTy IncLabel = this->getLabel(); + LoopScope<Emitter> LS(this, EndLabel, IncLabel); + + if (Init && !this->visitStmt(Init)) + return false; + this->emitLabel(CondLabel); + if (Cond) { + if (!this->visitBool(Cond)) + return false; + if (!this->jumpFalse(EndLabel)) + return false; } + if (Body && !this->visitStmt(Body)) + return false; + this->emitLabel(IncLabel); + if (Inc && !this->discard(Inc)) + return false; + if (!this->jump(CondLabel)) + return false; + this->emitLabel(EndLabel); + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) { + if (!BreakLabel) + return false; + + return this->jump(*BreakLabel); +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) { + if (!ContinueLabel) + return false; + + return this->jump(*ContinueLabel); } namespace clang { diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.h b/clang/lib/AST/Interp/ByteCodeStmtGen.h index 3bc665b84b4d..829e199f827c 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.h +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.h @@ -22,7 +22,6 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/Optional.h" namespace clang { namespace interp { @@ -33,10 +32,10 @@ template <class Emitter> class LabelScope; /// Compilation context for statements. template <class Emitter> -class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> { +class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> { using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - using OptLabelTy = llvm::Optional<LabelTy>; + using OptLabelTy = std::optional<LabelTy>; using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>; public: @@ -58,13 +57,14 @@ private: bool visitDeclStmt(const DeclStmt *DS); bool visitReturnStmt(const ReturnStmt *RS); bool visitIfStmt(const IfStmt *IS); + bool visitWhileStmt(const WhileStmt *S); + bool visitDoStmt(const DoStmt *S); + bool visitForStmt(const ForStmt *S); + bool visitBreakStmt(const BreakStmt *S); + bool visitContinueStmt(const ContinueStmt *S); - /// Compiles a variable declaration. - bool visitVarDecl(const VarDecl *VD); - -private: /// Type of the expression returned by the function. - llvm::Optional<PrimType> ReturnType; + std::optional<PrimType> ReturnType; /// Switch case mapping. CaseMap CaseLabels; diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 3bfcdfcd4c58..16471242f328 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -27,39 +27,52 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) {} Context::~Context() {} bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { + assert(Stk.empty()); Function *Func = P->getFunction(FD); - if (!Func) { + if (!Func || !Func->hasBody()) { if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) { Func = *R; } else { handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + Parent.FFDiag(Err.getRange().getBegin(), + diag::err_experimental_clang_interp_failed) + << Err.getRange(); }); return false; } } - if (!Func->isConstexpr()) - return false; - - APValue Dummy; - return Run(Parent, Func, Dummy); + return Func->isConstexpr(); } bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { + assert(Stk.empty()); ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretExpr(E)); + if (Check(Parent, C.interpretExpr(E))) { + assert(Stk.empty()); + return true; + } + + Stk.clear(); + return false; } bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result) { + assert(Stk.empty()); ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretDecl(VD)); + if (Check(Parent, C.interpretDecl(VD))) { + assert(Stk.empty()); + return true; + } + + Stk.clear(); + return false; } const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); } -llvm::Optional<PrimType> Context::classify(QualType T) { +std::optional<PrimType> Context::classify(QualType T) const { if (T->isReferenceType() || T->isPointerType()) { return PT_Ptr; } @@ -112,7 +125,7 @@ unsigned Context::getCharBit() const { bool Context::Run(State &Parent, Function *Func, APValue &Result) { InterpState State(Parent, *P, Stk, *this); - State.Current = new InterpFrame(State, Func, nullptr, {}, {}); + State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {}); if (Interpret(State, Result)) return true; Stk.clear(); @@ -123,7 +136,9 @@ bool Context::Check(State &Parent, llvm::Expected<bool> &&Flag) { if (Flag) return *Flag; handleAllErrors(Flag.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + Parent.FFDiag(Err.getRange().getBegin(), + diag::err_experimental_clang_interp_failed) + << Err.getRange(); }); return false; } diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h index 0627d9fb14f5..e49422e64b87 100644 --- a/clang/lib/AST/Interp/Context.h +++ b/clang/lib/AST/Interp/Context.h @@ -18,7 +18,6 @@ #include "InterpStack.h" #include "clang/AST/APValue.h" -#include "llvm/ADT/PointerIntPair.h" namespace clang { class ASTContext; @@ -33,7 +32,7 @@ class State; enum PrimType : unsigned; /// Holds all information required to evaluate constexpr code in a module. -class Context { +class Context final { public: /// Initialises the constexpr VM. Context(ASTContext &Ctx); @@ -60,7 +59,7 @@ public: unsigned getCharBit() const; /// Classifies an expression. - llvm::Optional<PrimType> classify(QualType T); + std::optional<PrimType> classify(QualType T) const; private: /// Runs a function. @@ -69,7 +68,6 @@ private: /// Checks a result from the interpreter. bool Check(State &Parent, llvm::Expected<bool> &&R); -private: /// Current compilation context. ASTContext &Ctx; /// Interpreter stack, shared across invocations. diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index 5c1a8a9cf306..04bc8681dd6e 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Descriptor.h" +#include "Boolean.h" #include "Pointer.h" #include "PrimType.h" #include "Record.h" @@ -39,6 +40,11 @@ static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) { template <typename T> static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) { + InitMap *IM = *reinterpret_cast<InitMap **>(Ptr); + if (IM != (InitMap *)-1) + free(IM); + + Ptr += sizeof(InitMap *); for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { reinterpret_cast<T *>(Ptr)[I].~T(); } @@ -72,9 +78,10 @@ static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable, Desc->IsBase = false; Desc->IsActive = IsActive; Desc->IsConst = IsConst || D->IsConst; - Desc->IsMutable = IsMutable || D->IsMutable; + Desc->IsFieldMutable = IsMutable || D->IsMutable; if (auto Fn = D->ElemDesc->CtorFn) - Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc); + Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, + D->ElemDesc); } } @@ -121,13 +128,14 @@ static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; Desc->Offset = SubOff; Desc->Desc = F; - Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; + Desc->IsInitialized = F->IsArray && !IsBase; Desc->IsBase = IsBase; Desc->IsActive = IsActive && !IsUnion; Desc->IsConst = IsConst || F->IsConst; - Desc->IsMutable = IsMutable || F->IsMutable; + Desc->IsFieldMutable = IsMutable || F->IsMutable; if (auto Fn = F->CtorFn) - Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F); + Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsFieldMutable, Desc->IsActive, + F); }; for (const auto &B : D->ElemRecord->bases()) CtorSub(B.Offset, B.Desc, /*isBase=*/true); @@ -178,26 +186,30 @@ static BlockCtorFn getCtorArrayPrim(PrimType Type) { } static BlockDtorFn getDtorArrayPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr); + TYPE_SWITCH(Type, return dtorArrayTy<T>); + llvm_unreachable("unknown Expr"); } static BlockMoveFn getMoveArrayPrim(PrimType Type) { COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, - bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), - MoveFn(getMovePrim(Type)) { +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(primSize(Type)), Size(ElemSize), + MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst), + IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)), + DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) { + assert(AllocSize >= Size); assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + size_t NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), - AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), + MDSize(MD.value_or(0)), + AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { @@ -206,39 +218,42 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(ElemSize * NumElems), - AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), - MoveFn(moveArrayDesc) { + Size(ElemSize * NumElems), MDSize(MD.value_or(0)), + AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), + ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), + DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { + Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), + ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), + IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), + MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, - bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), - Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), - DtorFn(dtorRecord), MoveFn(moveRecord) { + Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), + ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), + MoveFn(moveRecord) { assert(Source && "Missing source"); } @@ -259,9 +274,7 @@ SourceLocation Descriptor::getLocation() const { } InitMap::InitMap(unsigned N) : UninitFields(N) { - for (unsigned I = 0; I < N / PER_FIELD; ++I) { - data()[I] = 0; - } + std::fill_n(data(), (N + PER_FIELD - 1) / PER_FIELD, 0); } InitMap::T *InitMap::data() { @@ -269,9 +282,14 @@ InitMap::T *InitMap::data() { return reinterpret_cast<T *>(Start); } +const InitMap::T *InitMap::data() const { + auto *Start = reinterpret_cast<const char *>(this) + align(sizeof(InitMap)); + return reinterpret_cast<const T *>(Start); +} + bool InitMap::initialize(unsigned I) { unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); + T Mask = T(1) << (I % PER_FIELD); if (!(data()[Bucket] & Mask)) { data()[Bucket] |= Mask; UninitFields -= 1; @@ -279,10 +297,9 @@ bool InitMap::initialize(unsigned I) { return UninitFields == 0; } -bool InitMap::isInitialized(unsigned I) { +bool InitMap::isInitialized(unsigned I) const { unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); - return data()[Bucket] & Mask; + return data()[Bucket] & (T(1) << (I % PER_FIELD)); } InitMap *InitMap::allocate(unsigned N) { diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index 11072cab3e90..6ef4fc2f4c9b 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -47,8 +47,36 @@ using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr, /// Object size as used by the interpreter. using InterpSize = unsigned; +/// Inline descriptor embedded in structures and arrays. +/// +/// Such descriptors precede all composite array elements and structure fields. +/// If the base of a pointer is not zero, the base points to the end of this +/// structure. The offset field is used to traverse the pointer chain up +/// to the root structure which allocated the object. +struct InlineDescriptor { + /// Offset inside the structure/array. + unsigned Offset; + + /// Flag indicating if the storage is constant or not. + /// Relevant for primitive fields. + unsigned IsConst : 1; + /// For primitive fields, it indicates if the field was initialized. + /// Primitive fields in static storage are always initialized. + /// Arrays are always initialized, even though their elements might not be. + /// Base classes are initialized after the constructor is invoked. + unsigned IsInitialized : 1; + /// Flag indicating if the field is an embedded base class. + unsigned IsBase : 1; + /// Flag indicating if the field is the active member of a union. + unsigned IsActive : 1; + /// Flag indicating if the field is mutable (if in a record). + unsigned IsFieldMutable : 1; + + Descriptor *Desc; +}; + /// Describes a memory block created by an allocation site. -struct Descriptor { +struct Descriptor final { private: /// Original declaration, used to emit the error message. const DeclTy Source; @@ -56,6 +84,8 @@ private: const InterpSize ElemSize; /// Size of the storage, in host bytes. const InterpSize Size; + // Size of the metadata. + const InterpSize MDSize; /// Size of the allocation (storage + metadata), in host bytes. const InterpSize AllocSize; @@ -66,6 +96,9 @@ public: /// Token to denote structures of unknown size. struct UnknownSize {}; + using MetadataSize = std::optional<InterpSize>; + static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor); + /// Pointer to the record, if block contains records. Record *const ElemRecord = nullptr; /// Descriptor of the array element. @@ -85,26 +118,26 @@ public: const BlockMoveFn MoveFn = nullptr; /// Allocates a descriptor for a primitive. - Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives. - Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, + bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives of unknown size. Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); /// Allocates a descriptor for an array of composites. - Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of composites of unknown size. Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize); /// Allocates a descriptor for a record. - Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); QualType getType() const; SourceLocation getLocation() const; @@ -113,15 +146,15 @@ public: const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); } const ValueDecl *asValueDecl() const { - return dyn_cast_or_null<ValueDecl>(asDecl()); + return dyn_cast_if_present<ValueDecl>(asDecl()); } const FieldDecl *asFieldDecl() const { - return dyn_cast_or_null<FieldDecl>(asDecl()); + return dyn_cast_if_present<FieldDecl>(asDecl()); } const RecordDecl *asRecordDecl() const { - return dyn_cast_or_null<RecordDecl>(asDecl()); + return dyn_cast_if_present<RecordDecl>(asDecl()); } /// Returns the size of the object without metadata. @@ -134,6 +167,8 @@ public: unsigned getAllocSize() const { return AllocSize; } /// returns the size of an element when the structure is viewed as an array. unsigned getElemSize() const { return ElemSize; } + /// Returns the size of the metadata. + unsigned getMetadataSize() const { return MDSize; } /// Returns the number of elements stored in the block. unsigned getNumElems() const { @@ -154,39 +189,11 @@ public: bool isArray() const { return IsArray; } }; -/// Inline descriptor embedded in structures and arrays. -/// -/// Such descriptors precede all composite array elements and structure fields. -/// If the base of a pointer is not zero, the base points to the end of this -/// structure. The offset field is used to traverse the pointer chain up -/// to the root structure which allocated the object. -struct InlineDescriptor { - /// Offset inside the structure/array. - unsigned Offset; - - /// Flag indicating if the storage is constant or not. - /// Relevant for primitive fields. - unsigned IsConst : 1; - /// For primitive fields, it indicates if the field was initialized. - /// Primitive fields in static storage are always initialized. - /// Arrays are always initialized, even though their elements might not be. - /// Base classes are initialized after the constructor is invoked. - unsigned IsInitialized : 1; - /// Flag indicating if the field is an embedded base class. - unsigned IsBase : 1; - /// Flag indicating if the field is the active member of a union. - unsigned IsActive : 1; - /// Flag indicating if the field is mutable (if in a record). - unsigned IsMutable : 1; - - Descriptor *Desc; -}; - /// Bitfield tracking the initialisation status of elements of primitive arrays. /// A pointer to this is embedded at the end of all primitive arrays. /// If the map was not yet created and nothing was initialized, the pointer to /// this structure is 0. If the object was fully initialized, the pointer is -1. -struct InitMap { +struct InitMap final { private: /// Type packing bits. using T = uint64_t; @@ -198,13 +205,14 @@ private: /// Returns a pointer to storage. T *data(); + const T *data() const; public: /// Initializes an element. Returns true when object if fully initialized. bool initialize(unsigned I); /// Checks if an element was initialized. - bool isInitialized(unsigned I); + bool isInitialized(unsigned I) const; /// Allocates a map holding N elements. static InitMap *allocate(unsigned N); diff --git a/clang/lib/AST/Interp/Disasm.cpp b/clang/lib/AST/Interp/Disasm.cpp index 36adbe296b0c..d31e879d516f 100644 --- a/clang/lib/AST/Interp/Disasm.cpp +++ b/clang/lib/AST/Interp/Disasm.cpp @@ -21,17 +21,13 @@ using namespace clang; using namespace clang::interp; -template <typename T> -inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(Program &P, - CodePtr OpPC) { - return OpPC.read<T>(); -} - -template <typename T> -inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(Program &P, - CodePtr OpPC) { - uint32_t ID = OpPC.read<uint32_t>(); - return reinterpret_cast<T>(P.getNativePointer(ID)); +template <typename T> inline T ReadArg(Program &P, CodePtr &OpPC) { + if constexpr (std::is_pointer_v<T>) { + uint32_t ID = OpPC.read<uint32_t>(); + return reinterpret_cast<T>(P.getNativePointer(ID)); + } else { + return OpPC.read<T>(); + } } LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } @@ -40,10 +36,11 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { if (F) { if (auto *Cons = dyn_cast<CXXConstructorDecl>(F)) { DeclarationName Name = Cons->getParent()->getDeclName(); - OS << Name << "::" << Name << ":\n"; + OS << Name << "::" << Name; } else { - OS << F->getDeclName() << ":\n"; + OS << F->getDeclName(); } + OS << " " << (const void*)this << ":\n"; } else { OS << "<<expr>>\n"; } @@ -51,6 +48,7 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { OS << "frame size: " << getFrameSize() << "\n"; OS << "arg size: " << getArgSize() << "\n"; OS << "rvo: " << hasRVO() << "\n"; + OS << "this arg: " << hasThisPointer() << "\n"; auto PrintName = [&OS](const char *Name) { OS << Name; @@ -74,6 +72,10 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { + OS << ":: Program\n"; + OS << "Global Variables: " << Globals.size() << "\n"; + OS << "Functions: " << Funcs.size() << "\n"; + OS << "\n"; for (auto &Func : Funcs) { Func.second->dump(); } diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp index 22e8695b9211..72fd3b45254b 100644 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -23,7 +23,8 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, APValue &Result) : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { // Create a dummy frame for the interpreter which does not have locals. - S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer()); + S.Current = + new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr()); } llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) { @@ -54,6 +55,16 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) { auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); B->invokeCtor(); + // Initialize local variable inline descriptor. + InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); + Desc.Desc = D; + Desc.Offset = sizeof(InlineDescriptor); + Desc.IsActive = true; + Desc.IsBase = false; + Desc.IsFieldMutable = false; + Desc.IsConst = false; + Desc.IsInitialized = false; + // Register the local. unsigned Off = Locals.size(); Locals.insert({Off, std::move(Memory)}); @@ -123,7 +134,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { const Pointer &FP = Ptr.atField(F.Offset); QualType FieldTy = F.Decl->getType(); if (FP.isActive()) { - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { + if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); } else { Ok &= Composite(FieldTy, FP, Value); @@ -145,7 +156,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { const Pointer &FP = Ptr.atField(FD->Offset); APValue &Value = R.getStructField(I); - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { + if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); } else { Ok &= Composite(FieldTy, FP, Value); @@ -177,7 +188,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { for (unsigned I = 0; I < NumElems; ++I) { APValue &Slot = R.getArrayInitializedElt(I); const Pointer &EP = Ptr.atIndex(I); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot)); } else { Ok &= Composite(ElemTy, EP.narrow(), Slot); @@ -199,7 +210,8 @@ bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); - S.Stk.push<Pointer>(reinterpret_cast<Block *>(It->second.get())); + Block *B = reinterpret_cast<Block *>(It->second.get()); + S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); return true; } @@ -213,7 +225,7 @@ bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); auto *B = reinterpret_cast<Block *>(It->second.get()); - S.Stk.push<T>(*reinterpret_cast<T *>(B + 1)); + S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); return true; } @@ -227,7 +239,10 @@ bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); auto *B = reinterpret_cast<Block *>(It->second.get()); - *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>(); + *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>(); + InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); + Desc.IsInitialized = true; + return true; } diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h index eec2ff8ee753..6b6d0d621901 100644 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ b/clang/lib/AST/Interp/EvalEmitter.h @@ -23,7 +23,6 @@ #include "llvm/Support/Error.h" namespace clang { -class FunctionDecl; namespace interp { class Context; class Function; @@ -71,7 +70,7 @@ protected: Local createLocal(Descriptor *D); /// Returns the source location of the current opcode. - SourceInfo getSource(Function *F, CodePtr PC) const override { + SourceInfo getSource(const Function *F, CodePtr PC) const override { return F ? F->getSource(PC) : CurrentSource; } @@ -97,7 +96,7 @@ private: // value which is mapped to the location of the opcode being evaluated. CodePtr OpPC; /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; + std::optional<SourceLocation> BailLocation; /// Location of the current instruction. SourceInfo CurrentSource; @@ -110,12 +109,7 @@ private: /// Since expressions can only jump forward, predicated execution is /// used to deal with if-else statements. - bool isActive() { return CurrentLabel == ActiveLabel; } - - /// Helper to invoke a method. - bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info); - /// Helper to emit a diagnostic on a missing method. - bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info); + bool isActive() const { return CurrentLabel == ActiveLabel; } protected: #define GET_EVAL_PROTO diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 6ba97df1cd30..40001faad411 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -17,13 +17,11 @@ using namespace clang::interp; Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params) + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + bool HasThisPointer, bool HasRVO) : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), - ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {} - -CodePtr Function::getCodeBegin() const { return Code.data(); } - -CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); } + ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), + HasThisPointer(HasThisPointer), HasRVO(HasRVO) {} Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { auto It = Params.find(Offset); @@ -32,11 +30,12 @@ Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { } SourceInfo Function::getSource(CodePtr PC) const { + assert(PC >= getCodeBegin() && "PC does not belong to this function"); + assert(PC <= getCodeEnd() && "PC Does not belong to this function"); unsigned Offset = PC - getCodeBegin(); using Elem = std::pair<unsigned, SourceInfo>; auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first()); - if (It == SrcMap.end() || It->first != Offset) - llvm::report_fatal_error("missing source location"); + assert(It != SrcMap.end()); return It->second; } diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index ac1dffea1160..5b2a77f1a12d 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -29,7 +29,7 @@ enum PrimType : uint32_t; /// Describes a scope block. /// /// The block gathers all the descriptors of the locals defined in this block. -class Scope { +class Scope final { public: /// Information about a local's storage. struct Local { @@ -43,7 +43,7 @@ public: Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {} - llvm::iterator_range<LocalVectorTy::iterator> locals() { + llvm::iterator_range<LocalVectorTy::const_iterator> locals() const { return llvm::make_range(Descriptors.begin(), Descriptors.end()); } @@ -56,23 +56,42 @@ private: /// /// Contains links to the bytecode of the function, as well as metadata /// describing all arguments and stack-local variables. -class Function { +/// +/// # Calling Convention +/// +/// When calling a function, all argument values must be on the stack. +/// +/// If the function has a This pointer (i.e. hasThisPointer() returns true, +/// the argument values need to be preceeded by a Pointer for the This object. +/// +/// If the function uses Return Value Optimization, the arguments (and +/// potentially the This pointer) need to be proceeded by a Pointer pointing +/// to the location to construct the returned value. +/// +/// After the function has been called, it will remove all arguments, +/// including RVO and This pointer, from the stack. +/// +class Function final { public: using ParamDescriptor = std::pair<PrimType, Descriptor *>; /// Returns the size of the function's local stack. unsigned getFrameSize() const { return FrameSize; } - /// Returns the size of the argument stackx + /// Returns the size of the argument stack. unsigned getArgSize() const { return ArgSize; } /// Returns a pointer to the start of the code. - CodePtr getCodeBegin() const; + CodePtr getCodeBegin() const { return Code.data(); } /// Returns a pointer to the end of the code. - CodePtr getCodeEnd() const; + CodePtr getCodeEnd() const { return Code.data() + Code.size(); } /// Returns the original FunctionDecl. const FunctionDecl *getDecl() const { return F; } + /// Returns the name of the function decl this code + /// was generated for. + const std::string getName() const { return F->getNameInfo().getAsString(); } + /// Returns the location. SourceLocation getLoc() const { return Loc; } @@ -80,21 +99,24 @@ public: ParamDescriptor getParamDescriptor(unsigned Offset) const; /// Checks if the first argument is a RVO pointer. - bool hasRVO() const { return ParamTypes.size() != Params.size(); } + bool hasRVO() const { return HasRVO; } /// Range over the scope blocks. - llvm::iterator_range<llvm::SmallVector<Scope, 2>::iterator> scopes() { + llvm::iterator_range<llvm::SmallVector<Scope, 2>::const_iterator> + scopes() const { return llvm::make_range(Scopes.begin(), Scopes.end()); } /// Range over argument types. - using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator; - llvm::iterator_range<arg_reverse_iterator> args_reverse() { - return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend()); + using arg_reverse_iterator = + SmallVectorImpl<PrimType>::const_reverse_iterator; + llvm::iterator_range<arg_reverse_iterator> args_reverse() const { + return llvm::reverse(ParamTypes); } /// Returns a specific scope. Scope &getScope(unsigned Idx) { return Scopes[Idx]; } + const Scope &getScope(unsigned Idx) const { return Scopes[Idx]; } /// Returns the source information at a given PC. SourceInfo getSource(CodePtr PC) const; @@ -108,11 +130,22 @@ public: /// Checks if the function is a constructor. bool isConstructor() const { return isa<CXXConstructorDecl>(F); } + /// Checks if the function is fully done compiling. + bool isFullyCompiled() const { return IsFullyCompiled; } + + bool hasThisPointer() const { return HasThisPointer; } + + // Checks if the funtion already has a body attached. + bool hasBody() const { return HasBody; } + + unsigned getNumParams() const { return ParamTypes.size(); } + private: /// Construct a function representing an actual function. Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params); + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + bool HasThisPointer, bool HasRVO); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap, @@ -122,8 +155,11 @@ private: SrcMap = std::move(NewSrcMap); Scopes = std::move(NewScopes); IsValid = true; + HasBody = true; } + void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; } + private: friend class Program; friend class ByteCodeEmitter; @@ -135,7 +171,7 @@ private: /// Declaration this function was compiled from. const FunctionDecl *F; /// Local area size: storage + metadata. - unsigned FrameSize; + unsigned FrameSize = 0; /// Size of the argument stack. unsigned ArgSize; /// Program code. @@ -150,6 +186,18 @@ private: llvm::DenseMap<unsigned, ParamDescriptor> Params; /// Flag to indicate if the function is valid. bool IsValid = false; + /// Flag to indicate if the function is done being + /// compiled to bytecode. + bool IsFullyCompiled = false; + /// Flag indicating if this function takes the this pointer + /// as the first implicit argument + bool HasThisPointer = false; + /// Whether this function has Return Value Optimization, i.e. + /// the return value is constructed in the caller's stack frame. + /// This is done for functions that return non-primive values. + bool HasRVO = false; + /// If we've already compiled the function's body. + bool HasBody = false; public: /// Dumps the disassembled bytecode to \c llvm::errs(). diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index 46cd611ee389..8a742333ae57 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -53,17 +53,17 @@ template <> struct Repr<64, true> { using Type = int64_t; }; /// These wrappers are required to shared an interface between APSint and /// builtin primitive numeral types, while optimising for storage and /// allowing methods operating on primitive type to compile to fast code. -template <unsigned Bits, bool Signed> class Integral { +template <unsigned Bits, bool Signed> class Integral final { private: template <unsigned OtherBits, bool OtherSigned> friend class Integral; // The primitive representing the integral. - using T = typename Repr<Bits, Signed>::Type; - T V; + using ReprT = typename Repr<Bits, Signed>::Type; + ReprT V; /// Primitive representing limits. - static const auto Min = std::numeric_limits<T>::min(); - static const auto Max = std::numeric_limits<T>::max(); + static const auto Min = std::numeric_limits<ReprT>::min(); + static const auto Max = std::numeric_limits<ReprT>::max(); /// Construct an integral from anything that is convertible to storage. template <typename T> explicit Integral(T V) : V(V) {} @@ -107,7 +107,7 @@ public: return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed); } APSInt toAPSInt(unsigned NumBits) const { - if (Signed) + if constexpr (Signed) return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed); else return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed); @@ -124,25 +124,27 @@ public: bool isMin() const { return *this == min(bitWidth()); } - bool isMinusOne() const { return Signed && V == T(-1); } + bool isMinusOne() const { return Signed && V == ReprT(-1); } constexpr static bool isSigned() { return Signed; } - bool isNegative() const { return V < T(0); } + bool isNegative() const { return V < ReprT(0); } bool isPositive() const { return !isNegative(); } ComparisonCategoryResult compare(const Integral &RHS) const { return Compare(V, RHS.V); } - unsigned countLeadingZeros() const { return llvm::countLeadingZeros<T>(V); } + unsigned countLeadingZeros() const { + return llvm::countLeadingZeros<ReprT>(V); + } Integral truncate(unsigned TruncBits) const { if (TruncBits >= Bits) return *this; - const T BitMask = (T(1) << T(TruncBits)) - 1; - const T SignBit = T(1) << (TruncBits - 1); - const T ExtMask = ~BitMask; + const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1; + const ReprT SignBit = ReprT(1) << (TruncBits - 1); + const ReprT ExtMask = ~BitMask; return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0)); } @@ -155,9 +157,11 @@ public: return Integral(Max); } - template <typename T> - static std::enable_if_t<std::is_integral<T>::value, Integral> from(T Value) { - return Integral(Value); + template <typename ValT> static Integral from(ValT Value) { + if constexpr (std::is_integral<ValT>::value) + return Integral(Value); + else + return Integral::from(static_cast<Integral::ReprT>(Value)); } template <unsigned SrcBits, bool SrcSign> @@ -167,7 +171,7 @@ public: } template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) { - if (SrcSign) + if constexpr (SrcSign) return Integral(Value.V.getSExtValue()); else return Integral(Value.V.getZExtValue()); @@ -180,15 +184,15 @@ public: } static bool inRange(int64_t Value, unsigned NumBits) { - return CheckRange<T, Min, Max>(Value); + return CheckRange<ReprT, Min, Max>(Value); } static bool increment(Integral A, Integral *R) { - return add(A, Integral(T(1)), A.bitWidth(), R); + return add(A, Integral(ReprT(1)), A.bitWidth(), R); } static bool decrement(Integral A, Integral *R) { - return sub(A, Integral(T(1)), A.bitWidth(), R); + return sub(A, Integral(ReprT(1)), A.bitWidth(), R); } static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) { @@ -203,56 +207,74 @@ public: return CheckMulUB(A.V, B.V, R->V); } -private: - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckAddUB(T A, T B, - T &R) { - return llvm::AddOverflow<T>(A, B, R); + static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V % B.V); + return false; + } + + static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V / B.V); + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckAddUB(T A, T B, - T &R) { - R = A + B; + static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V & B.V); return false; } - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckSubUB(T A, T B, - T &R) { - return llvm::SubOverflow<T>(A, B, R); + static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V | B.V); + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckSubUB(T A, T B, - T &R) { - R = A - B; + static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V ^ B.V); return false; } - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckMulUB(T A, T B, - T &R) { - return llvm::MulOverflow<T>(A, B, R); + static bool neg(Integral A, Integral *R) { + *R = -A; + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckMulUB(T A, T B, - T &R) { - R = A * B; + static bool comp(Integral A, Integral *R) { + *R = Integral(~A.V); return false; } - template <typename T, T Min, T Max> - static std::enable_if_t<std::is_signed<T>::value, bool> - CheckRange(int64_t V) { - return Min <= V && V <= Max; +private: + template <typename T> static bool CheckAddUB(T A, T B, T &R) { + if constexpr (std::is_signed_v<T>) { + return llvm::AddOverflow<T>(A, B, R); + } else { + R = A + B; + return false; + } } - template <typename T, T Min, T Max> - static std::enable_if_t<std::is_unsigned<T>::value, bool> - CheckRange(int64_t V) { - return V >= 0 && static_cast<uint64_t>(V) <= Max; + template <typename T> static bool CheckSubUB(T A, T B, T &R) { + if constexpr (std::is_signed_v<T>) { + return llvm::SubOverflow<T>(A, B, R); + } else { + R = A - B; + return false; + } + } + + template <typename T> static bool CheckMulUB(T A, T B, T &R) { + if constexpr (std::is_signed_v<T>) { + return llvm::MulOverflow<T>(A, B, R); + } else { + R = A * B; + return false; + } + } + template <typename T, T Min, T Max> static bool CheckRange(int64_t V) { + if constexpr (std::is_signed_v<T>) { + return Min <= V && V <= Max; + } else { + return V >= 0 && static_cast<uint64_t>(V) <= Max; + } } }; diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index cec3f6d6160e..6a600b306bad 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -1,4 +1,4 @@ -//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// +//===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -201,8 +201,8 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { - const auto &Src = S.Current->getSource(OpPC); if (Ptr.isZero()) { + const auto &Src = S.Current->getSource(OpPC); if (Ptr.isField()) S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field; @@ -213,6 +213,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, } if (!Ptr.isLive()) { + const auto &Src = S.Current->getSource(OpPC); bool IsTemp = Ptr.isTemporary(); S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp; @@ -330,17 +331,18 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return true; } -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) { - const SourceLocation &Loc = S.Current->getLocation(OpPC); +bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { if (F->isVirtual()) { if (!S.getLangOpts().CPlusPlus20) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); S.CCEDiag(Loc, diag::note_constexpr_virtual_call); return false; } } if (!F->isConstexpr()) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); if (S.getLangOpts().CPlusPlus11) { const FunctionDecl *DiagDecl = F->getDecl(); @@ -398,9 +400,92 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { S.Note(MD->getLocation(), diag::note_declared_at); return false; } + +static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI, + QualType SubObjType, + SourceLocation SubObjLoc) { + S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType; + if (SubObjLoc.isValid()) + S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here); +} + +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R); + +static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, + const ConstantArrayType *CAT) { + bool Result = true; + size_t NumElems = CAT->getSize().getZExtValue(); + QualType ElemType = CAT->getElementType(); + + if (isa<RecordType>(ElemType.getTypePtr())) { + const Record *R = BasePtr.getElemRecord(); + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R); + } + } else if (auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) { + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT); + } + } else { + for (size_t I = 0; I != NumElems; ++I) { + if (!BasePtr.atIndex(I).isInitialized()) { + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType, + BasePtr.getFieldDesc()->getLocation()); + Result = false; + } + } + } + + return Result; +} + +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R) { + assert(R); + bool Result = true; + // Check all fields of this record are initialized. + for (const Record::Field &F : R->fields()) { + Pointer FieldPtr = BasePtr.atField(F.Offset); + QualType FieldType = F.Decl->getType(); + + if (FieldType->isRecordType()) { + Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord()); + } else if (FieldType->isArrayType()) { + const auto *CAT = + cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe()); + Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT); + } else if (!FieldPtr.isInitialized()) { + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), + F.Decl->getType(), F.Decl->getLocation()); + Result = false; + } + } + return Result; +} + +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) { + assert(!This.isZero()); + const Record *R = This.getRecord(); + return CheckFieldsInitialized(S, OpPC, This, R); +} + bool Interpret(InterpState &S, APValue &Result) { + // The current stack frame when we started Interpret(). + // This is being used by the ops to determine wheter + // to return from this function and thus terminate + // interpretation. + const InterpFrame *StartFrame = S.Current; + assert(!S.Current->isRoot()); CodePtr PC = S.Current->getPC(); + // Empty program. + if (!PC) + return true; + for (;;) { auto Op = PC.read<Opcode>(); CodePtr OpPC = PC; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index a1d90f26ba46..ed3accd98a90 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_INTERP_H #define LLVM_CLANG_AST_INTERP_INTERP_H +#include "Boolean.h" #include "Function.h" #include "InterpFrame.h" #include "InterpStack.h" @@ -30,7 +31,6 @@ #include "llvm/Support/Endian.h" #include <limits> #include <type_traits> -#include <vector> namespace clang { namespace interp { @@ -84,7 +84,7 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a method can be called. -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F); +bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F); /// Checks the 'this' pointer. bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); @@ -92,7 +92,53 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); /// Checks if a method is pure virtual. bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); -template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); } +/// Checks that all fields are initialized after a constructor call. +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This); + +/// Checks if the shift operation is legal. +template <typename RT> +bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) { + if (RHS.isNegative()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); + return false; + } + + // C++11 [expr.shift]p1: Shift width must be less than the bit width of + // the shifted type. + if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) { + const Expr *E = S.Current->getExpr(OpPC); + const APSInt Val = RHS.toAPSInt(); + QualType Ty = E->getType(); + S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; + return false; + } + return true; +} + +/// Checks if Div/Rem operation on LHS and RHS is valid. +template <typename T> +bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) { + if (RHS.isZero()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_expr_divide_by_zero); + return false; + } + + if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) { + APSInt LHSInt = LHS.toAPSInt(); + SmallString<32> Trunc; + (-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10); + const SourceInfo &Loc = S.Current->getSource(OpPC); + const Expr *E = S.Current->getExpr(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType(); + return false; + } + return true; +} + +/// Interpreter entry point. +bool Interpret(InterpState &S, APValue &Result); //===----------------------------------------------------------------------===// // Add, Sub, Mul @@ -154,6 +200,240 @@ bool Mul(InterpState &S, CodePtr OpPC) { return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS); } +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS & RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitAnd(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitAnd(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS | RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitOr(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitOr(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS ^ RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitXor(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitXor(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS). +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Rem(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + if (!CheckDivRem(S, OpPC, LHS, RHS)) + return false; + + const unsigned Bits = RHS.bitWidth() * 2; + T Result; + if (!T::rem(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS / RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Div(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + if (!CheckDivRem(S, OpPC, LHS, RHS)) + return false; + + const unsigned Bits = RHS.bitWidth() * 2; + T Result; + if (!T::div(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// Inv +//===----------------------------------------------------------------------===// + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Inv(InterpState &S, CodePtr OpPC) { + using BoolT = PrimConv<PT_Bool>::T; + const T &Val = S.Stk.pop<T>(); + const unsigned Bits = Val.bitWidth(); + Boolean R; + Boolean::inv(BoolT::from(Val, Bits), &R); + + S.Stk.push<BoolT>(R); + return true; +} + +//===----------------------------------------------------------------------===// +// Neg +//===----------------------------------------------------------------------===// + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Neg(InterpState &S, CodePtr OpPC) { + const T &Val = S.Stk.pop<T>(); + T Result; + T::neg(Val, &Result); + + S.Stk.push<T>(Result); + return true; +} + +enum class PushVal : bool { + No, + Yes, +}; +enum class IncDecOp { + Inc, + Dec, +}; + +template <typename T, IncDecOp Op, PushVal DoPush> +bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + T Value = Ptr.deref<T>(); + T Result; + + if constexpr (DoPush == PushVal::Yes) + S.Stk.push<T>(Result); + + if constexpr (Op == IncDecOp::Inc) { + if (!T::increment(Value, &Result)) { + Ptr.deref<T>() = Result; + return true; + } + } else { + if (!T::decrement(Value, &Result)) { + Ptr.deref<T>() = Result; + return true; + } + } + + // Something went wrong with the previous operation. Compute the + // result with another bit of precision. + unsigned Bits = Value.bitWidth() + 1; + APSInt APResult; + if constexpr (Op == IncDecOp::Inc) + APResult = ++Value.toAPSInt(Bits); + else + APResult = --Value.toAPSInt(Bits); + + // Report undefined behaviour, stopping if required. + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + if (S.checkingForUndefinedBehavior()) { + SmallString<32> Trunc; + APResult.trunc(Result.bitWidth()).toString(Trunc, 10); + auto Loc = E->getExprLoc(); + S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; + return true; + } + + S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type; + return S.noteUndefinedBehavior(); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +/// 4) Pushes the original (pre-inc) value on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Inc(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool IncPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +/// 4) Pushes the original (pre-dec) value on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Dec(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool DecPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr); +} + +/// 1) Pops the value from the stack. +/// 2) Pushes the bitwise complemented value on the stack (~V). +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Comp(InterpState &S, CodePtr OpPC) { + const T &Val = S.Stk.pop<T>(); + T Result; + if (!T::comp(Val, &Result)) { + S.Stk.push<T>(Result); + return true; + } + + return false; +} + //===----------------------------------------------------------------------===// // EQ, NE, GT, GE, LT, LE //===----------------------------------------------------------------------===// @@ -209,6 +489,16 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { } else { unsigned VL = LHS.getByteOffset(); unsigned VR = RHS.getByteOffset(); + + // In our Pointer class, a pointer to an array and a pointer to the first + // element in the same array are NOT equal. They have the same Base value, + // but a different Offset. This is a pretty rare case, so we fix this here + // by comparing pointers to the first elements. + if (LHS.inArray() && LHS.isRoot()) + VL = LHS.atIndex(0).getByteOffset(); + if (RHS.inArray() && RHS.isRoot()) + VR = RHS.atIndex(0).getByteOffset(); + S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR)))); return true; } @@ -304,7 +594,10 @@ bool Const(InterpState &S, CodePtr OpPC, const T &Arg) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push<T>(S.Current->getLocal<T>(I)); + const Pointer &Ptr = S.Current->getLocalPointer(I); + if (!CheckLoad(S, OpPC, Ptr)) + return false; + S.Stk.push<T>(Ptr.deref<T>()); return true; } @@ -329,6 +622,8 @@ bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Peeks a pointer on the stack +/// 2) Pushes the value of the pointer's field on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.peek<Pointer>(); @@ -358,6 +653,8 @@ bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops a pointer from the stack +/// 2) Pushes the value of the pointer's field on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.pop<Pointer>(); @@ -463,10 +760,13 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops the value from the stack +/// 2) Peeks a pointer from the stack +/// 3) Pushes the value to field I of the pointer on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop<T>(); - const Pointer &Field = S.Stk.pop<Pointer>().atField(I); + const Pointer &Field = S.Stk.peek<Pointer>().atField(I); Field.deref<T>() = Value; Field.activate(); Field.initialize(); @@ -516,6 +816,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops a Pointer from the stack +/// 2) Pushes Pointer.atField(Off) on the stack inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckNull(S, OpPC, Ptr, CSK_Field)) @@ -638,6 +940,8 @@ bool Store(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref<T>() = Value; return true; } @@ -648,6 +952,8 @@ bool StorePop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref<T>() = Value; return true; } @@ -658,6 +964,8 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); if (auto *FD = Ptr.getField()) { Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { @@ -672,6 +980,8 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); if (auto *FD = Ptr.getField()) { Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { @@ -691,6 +1001,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) { return true; } +/// 1) Pops the value from the stack +/// 2) Peeks a pointer and gets its index \Idx +/// 3) Sets the value on the pointer, leaving the pointer on the stack. template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop<T>(); @@ -702,6 +1015,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { return true; } +/// The same as InitElem, but pops the pointer as well. template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop<T>(); @@ -721,23 +1035,25 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) { // Fetch the pointer and the offset. const T &Offset = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) - return false; + if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) return false; - // Get a version of the index comparable to the type. - T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); - // A zero offset does not change the pointer, but in the case of an array - // it has to be adjusted to point to the first element instead of the array. + // A zero offset does not change the pointer. if (Offset.isZero()) { - S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr); + S.Stk.push<Pointer>(Ptr); return true; } + + if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) + return false; + // Arrays of unknown bounds cannot have pointers into them. if (!CheckArray(S, OpPC, Ptr)) return false; + // Get a version of the index comparable to the type. + T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); // Compute the largest index into the array. unsigned MaxIndex = Ptr.getNumElems(); @@ -754,23 +1070,34 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) { return false; }; - // If the new offset would be negative, bail out. - if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index)) - return InvalidOffset(); - if (!Add && Offset.isPositive() && Index < Offset) - return InvalidOffset(); - - // If the new offset would be out of bounds, bail out. unsigned MaxOffset = MaxIndex - Ptr.getIndex(); - if (Add && Offset.isPositive() && Offset > MaxOffset) - return InvalidOffset(); - if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) - return InvalidOffset(); + if constexpr (Add) { + // If the new offset would be negative, bail out. + if (Offset.isNegative() && (Offset.isMin() || -Offset > Index)) + return InvalidOffset(); + + // If the new offset would be out of bounds, bail out. + if (Offset.isPositive() && Offset > MaxOffset) + return InvalidOffset(); + } else { + // If the new offset would be negative, bail out. + if (Offset.isPositive() && Index < Offset) + return InvalidOffset(); + + // If the new offset would be out of bounds, bail out. + if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) + return InvalidOffset(); + } // Offset is valid - compute it on unsigned. int64_t WideIndex = static_cast<int64_t>(Index); int64_t WideOffset = static_cast<int64_t>(Offset); - int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset); + int64_t Result; + if constexpr (Add) + Result = WideIndex + WideOffset; + else + Result = WideIndex - WideOffset; + S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result))); return true; } @@ -785,6 +1112,23 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { return OffsetHelper<T, false>(S, OpPC); } +/// 1) Pops a Pointer from the stack. +/// 2) Pops another Pointer from the stack. +/// 3) Pushes the different of the indices of the two pointers on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +inline bool SubPtr(InterpState &S, CodePtr OpPC) { + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &RHS = S.Stk.pop<Pointer>(); + + if (!Pointer::hasSameArray(LHS, RHS)) { + // TODO: Diagnose. + return false; + } + + T A = T::from(LHS.getIndex()); + T B = T::from(RHS.getIndex()); + return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B); +} //===----------------------------------------------------------------------===// // Destroy @@ -840,88 +1184,47 @@ inline bool This(InterpState &S, CodePtr OpPC) { return true; } +inline bool RVOPtr(InterpState &S, CodePtr OpPC) { + assert(S.Current->getFunction()->hasRVO()); + S.Stk.push<Pointer>(S.Current->getRVOPtr()); + return true; +} + //===----------------------------------------------------------------------===// // Shr, Shl //===----------------------------------------------------------------------===// -template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T> -unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { - // C++11 [expr.shift]p1: Shift width must be less than the bit width of - // the shifted type. - if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { - const Expr *E = S.Current->getExpr(OpPC); - const APSInt Val = V.toAPSInt(); - QualType Ty = E->getType(); - S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; - return Bits; - } else { - return static_cast<unsigned>(V); - } -} - -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V >> RHS, V.bitWidth())); - } - return true; -} +template <PrimType NameL, PrimType NameR> +inline bool Shr(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv<NameL>::T; + using RT = typename PrimConv<NameR>::T; + const auto &RHS = S.Stk.pop<RT>(); + const auto &LHS = S.Stk.pop<LT>(); + const unsigned Bits = LHS.bitWidth(); -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (V.isSigned() && !S.getLangOpts().CPlusPlus20) { - // C++11 [expr.shift]p2: A signed left shift must have a non-negative - // operand, and must not overflow the corresponding unsigned type. - // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to - // E1 x 2^E2 module 2^N. - if (V.isNegative()) { - const Expr *E = S.Current->getExpr(OpPC); - S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt(); - } else if (V.countLeadingZeros() < RHS) { - S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards); - } - } + if (!CheckShift<RT>(S, OpPC, RHS, Bits)) + return false; - if (V.bitWidth() == 1) { - S.Stk.push<T>(V); - } else if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth())); - } + unsigned URHS = static_cast<unsigned>(RHS); + S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth())); return true; } -template <PrimType TL, PrimType TR> -inline bool Shr(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); +template <PrimType NameL, PrimType NameR> +inline bool Shl(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv<NameL>::T; + using RT = typename PrimConv<NameR>::T; + const auto &RHS = S.Stk.pop<RT>(); + const auto &LHS = S.Stk.pop<LT>(); const unsigned Bits = LHS.bitWidth(); - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } -} + if (!CheckShift<RT>(S, OpPC, RHS, Bits)) + return false; -template <PrimType TL, PrimType TR> -inline bool Shl(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); - const unsigned Bits = LHS.bitWidth(); + unsigned URHS = static_cast<unsigned>(RHS); + S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth())); - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } + return true; } //===----------------------------------------------------------------------===// @@ -950,26 +1253,56 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { return true; } +inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) { + auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); + Pointer ThisPtr; + if (Func->hasThisPointer()) { + ThisPtr = NewFrame->getThis(); + if (!CheckInvoke(S, PC, ThisPtr)) { + return false; + } + } + + if (!CheckCallable(S, PC, Func)) + return false; + + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + APValue CallResult; + // Note that we cannot assert(CallResult.hasValue()) here since + // Ret() above only sets the APValue if the curent frame doesn't + // have a caller set. + if (Interpret(S, CallResult)) { + NewFrame.release(); // Frame was delete'd already. + assert(S.Current == FrameBefore); + + // For constructors, check that all fields have been initialized. + if (Func->isConstructor() && !CheckCtorCall(S, PC, ThisPtr)) + return false; + + return true; + } + + // Interpreting the function failed somehow. Reset to + // previous state. + S.Current = FrameBefore; + return false; +} + //===----------------------------------------------------------------------===// // Read opcode arguments //===----------------------------------------------------------------------===// -template <typename T> -inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { - return OpPC.read<T>(); -} - -template <typename T> -inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { - uint32_t ID = OpPC.read<uint32_t>(); - return reinterpret_cast<T>(S.P.getNativePointer(ID)); +template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) { + if constexpr (std::is_pointer<T>::value) { + uint32_t ID = OpPC.read<uint32_t>(); + return reinterpret_cast<T>(S.P.getNativePointer(ID)); + } else { + return OpPC.read<T>(); + } } -/// Interpreter entry point. -bool Interpret(InterpState &S, APValue &Result); - } // namespace interp } // namespace clang diff --git a/clang/lib/AST/Interp/InterpBlock.h b/clang/lib/AST/Interp/InterpBlock.h index 2d5386e60b8c..f790c50a9123 100644 --- a/clang/lib/AST/Interp/InterpBlock.h +++ b/clang/lib/AST/Interp/InterpBlock.h @@ -31,11 +31,25 @@ enum PrimType : unsigned; /// A memory block, either on the stack or in the heap. /// -/// The storage described by the block immediately follows it in memory. -class Block { +/// The storage described by the block is immediately followed by +/// optional metadata, which is followed by the actual data. +/// +/// Block* rawData() data() +/// │ │ │ +/// │ │ │ +/// ▼ ▼ ▼ +/// ┌───────────────┬─────────────────────────┬─────────────────┐ +/// │ Block │ Metadata │ Data │ +/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │ +/// └───────────────┴─────────────────────────┴─────────────────┘ +/// +/// Desc->getAllocSize() describes the size after the Block, i.e. +/// the data size and the metadata size. +/// +class Block final { public: // Creates a new block. - Block(const llvm::Optional<unsigned> &DeclID, Descriptor *Desc, + Block(const std::optional<unsigned> &DeclID, Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} @@ -56,10 +70,27 @@ public: /// Returns the size of the block. InterpSize getSize() const { return Desc->getAllocSize(); } /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return DeclID; } + std::optional<unsigned> getDeclID() const { return DeclID; } /// Returns a pointer to the stored data. - char *data() { return reinterpret_cast<char *>(this + 1); } + /// You are allowed to read Desc->getSize() bytes from this address. + char *data() { + // rawData might contain metadata as well. + size_t DataOffset = Desc->getMetadataSize(); + return rawData() + DataOffset; + } + const char *data() const { + // rawData might contain metadata as well. + size_t DataOffset = Desc->getMetadataSize(); + return rawData() + DataOffset; + } + + /// Returns a pointer to the raw data, including metadata. + /// You are allowed to read Desc->getAllocSize() bytes from this address. + char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); } + const char *rawData() const { + return reinterpret_cast<const char *>(this) + sizeof(Block); + } /// Returns a view over the data. template <typename T> @@ -67,12 +98,18 @@ public: /// Invokes the constructor. void invokeCtor() { - std::memset(data(), 0, getSize()); + std::memset(rawData(), 0, Desc->getAllocSize()); if (Desc->CtorFn) Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, /*isActive=*/true, Desc); } + // Invokes the Destructor. + void invokeDtor() { + if (Desc->DtorFn) + Desc->DtorFn(this, data(), Desc); + } + protected: friend class Pointer; friend class DeadBlock; @@ -92,7 +129,7 @@ protected: /// Start of the chain of pointers. Pointer *Pointers = nullptr; /// Unique identifier of the declaration. - llvm::Optional<unsigned> DeclID; + std::optional<unsigned> DeclID; /// Flag indicating if the block has static storage duration. bool IsStatic = false; /// Flag indicating if the block is an extern. @@ -107,7 +144,7 @@ protected: /// /// Dead blocks are chained in a double-linked list to deallocate them /// whenever pointers become dead. -class DeadBlock { +class DeadBlock final { public: /// Copies the block. DeadBlock(DeadBlock *&Root, Block *Blk); diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index 9d01bf0333fe..40644c538c6a 100644 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ b/clang/lib/AST/Interp/InterpFrame.cpp @@ -7,37 +7,68 @@ //===----------------------------------------------------------------------===// #include "InterpFrame.h" +#include "Boolean.h" #include "Function.h" -#include "Interp.h" #include "InterpStack.h" +#include "InterpState.h" +#include "Pointer.h" #include "PrimType.h" #include "Program.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" using namespace clang; using namespace clang::interp; -InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This) - : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC), +InterpFrame::InterpFrame(InterpState &S, const Function *Func, + InterpFrame *Caller, CodePtr RetPC) + : Caller(Caller), S(S), Func(Func), RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0), Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) { - if (Func) { - if (unsigned FrameSize = Func->getFrameSize()) { - Locals = std::make_unique<char[]>(FrameSize); - for (auto &Scope : Func->scopes()) { - for (auto &Local : Scope.locals()) { - Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); - B->invokeCtor(); - } - } + if (!Func) + return; + + unsigned FrameSize = Func->getFrameSize(); + if (FrameSize == 0) + return; + + Locals = std::make_unique<char[]>(FrameSize); + for (auto &Scope : Func->scopes()) { + for (auto &Local : Scope.locals()) { + Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); + B->invokeCtor(); + InlineDescriptor *ID = localInlineDesc(Local.Offset); + ID->Desc = Local.Desc; + ID->IsActive = true; + ID->Offset = sizeof(InlineDescriptor); + ID->IsBase = false; + ID->IsFieldMutable = false; + ID->IsConst = false; + ID->IsInitialized = false; } } } +InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC) + : InterpFrame(S, Func, S.Current, RetPC) { + // As per our calling convention, the this pointer is + // part of the ArgSize. + // If the function has RVO, the RVO pointer is first. + // If the fuction has a This pointer, that one is next. + // Then follow the actual arguments (but those are handled + // in getParamPointer()). + if (Func->hasRVO()) + RVOPtr = stackRef<Pointer>(0); + + if (Func->hasThisPointer()) { + if (Func->hasRVO()) + This = stackRef<Pointer>(sizeof(Pointer)); + else + This = stackRef<Pointer>(0); + } +} + InterpFrame::~InterpFrame() { - if (Func && Func->isConstructor() && This.isBaseClass()) - This.initialize(); for (auto &Param : Params) S.deallocate(reinterpret_cast<Block *>(Param.second.get())); } @@ -95,17 +126,17 @@ void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx, } printDesc(P.getDeclDesc()); - for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) { - if (It->inArray()) { - OS << "[" << It->expand().getIndex() << "]"; + for (const auto &It : Levels) { + if (It.inArray()) { + OS << "[" << It.expand().getIndex() << "]"; continue; } - if (auto Index = It->getIndex()) { + if (auto Index = It.getIndex()) { OS << " + " << Index; continue; } OS << "."; - printDesc(It->getFieldDesc()); + printDesc(It.getFieldDesc()); } } @@ -117,16 +148,15 @@ void InterpFrame::describe(llvm::raw_ostream &OS) { OS << "->"; } OS << *F << "("; - unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0; + unsigned Off = 0; + + Off += Func->hasRVO() ? primSize(PT_Ptr) : 0; + Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0; + for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) { QualType Ty = F->getParamDecl(I)->getType(); - PrimType PrimTy; - if (llvm::Optional<PrimType> T = S.Ctx.classify(Ty)) { - PrimTy = *T; - } else { - PrimTy = PT_Ptr; - } + PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr); TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty)); Off += align(primSize(PrimTy)); @@ -152,10 +182,10 @@ const FunctionDecl *InterpFrame::getCallee() const { return Func->getDecl(); } -Pointer InterpFrame::getLocalPointer(unsigned Offset) { +Pointer InterpFrame::getLocalPointer(unsigned Offset) const { assert(Offset < Func->getFrameSize() && "Invalid local offset."); - return Pointer( - reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block))); + return Pointer(reinterpret_cast<Block *>(localBlock(Offset)), + sizeof(InlineDescriptor)); } Pointer InterpFrame::getParamPointer(unsigned Off) { diff --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h index 304e2ad66537..bfa02c90ebec 100644 --- a/clang/lib/AST/Interp/InterpFrame.h +++ b/clang/lib/AST/Interp/InterpFrame.h @@ -14,7 +14,6 @@ #define LLVM_CLANG_AST_INTERP_INTERPFRAME_H #include "Frame.h" -#include "Pointer.h" #include "Program.h" #include "State.h" #include <cstdint> @@ -24,6 +23,7 @@ namespace clang { namespace interp { class Function; class InterpState; +class Pointer; /// Frame storing local variables. class InterpFrame final : public Frame { @@ -32,8 +32,14 @@ public: InterpFrame *Caller; /// Creates a new frame for a method call. - InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This); + InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller, + CodePtr RetPC); + + /// Creates a new frame with the values that make sense. + /// I.e., the caller is the current frame of S, + /// the This() pointer is the current Pointer on the top of S's stack, + /// and the RVO pointer is before that. + InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC); /// Destroys the frame, killing all live pointers to stack slots. ~InterpFrame(); @@ -57,26 +63,27 @@ public: const FunctionDecl *getCallee() const override; /// Returns the current function. - Function *getFunction() const { return Func; } + const Function *getFunction() const { return Func; } /// Returns the offset on the stack at which the frame starts. size_t getFrameOffset() const { return FrameOffset; } /// Returns the value of a local variable. - template <typename T> const T &getLocal(unsigned Offset) { + template <typename T> const T &getLocal(unsigned Offset) const { return localRef<T>(Offset); } /// Mutates a local variable. template <typename T> void setLocal(unsigned Offset, const T &Value) { localRef<T>(Offset) = Value; + localInlineDesc(Offset)->IsInitialized = true; } /// Returns a pointer to a local variables. - Pointer getLocalPointer(unsigned Offset); + Pointer getLocalPointer(unsigned Offset) const; /// Returns the value of an argument. - template <typename T> const T &getParam(unsigned Offset) { + template <typename T> const T &getParam(unsigned Offset) const { auto Pt = Params.find(Offset); if (Pt == Params.end()) { return stackRef<T>(Offset); @@ -96,6 +103,9 @@ public: /// Returns the 'this' pointer. const Pointer &getThis() const { return This; } + /// Returns the RVO pointer, if the Function has one. + const Pointer &getRVOPtr() const { return RVOPtr; } + /// Checks if the frame is a root frame - return should quit the interpreter. bool isRoot() const { return !Func; } @@ -112,27 +122,35 @@ public: private: /// Returns an original argument from the stack. - template <typename T> const T &stackRef(unsigned Offset) { + template <typename T> const T &stackRef(unsigned Offset) const { + assert(Args); return *reinterpret_cast<const T *>(Args - ArgSize + Offset); } /// Returns an offset to a local. - template <typename T> T &localRef(unsigned Offset) { - return *reinterpret_cast<T *>(Locals.get() + Offset); + template <typename T> T &localRef(unsigned Offset) const { + return getLocalPointer(Offset).deref<T>(); } /// Returns a pointer to a local's block. - void *localBlock(unsigned Offset) { + void *localBlock(unsigned Offset) const { return Locals.get() + Offset - sizeof(Block); } + // Returns the inline descriptor of the local. + InlineDescriptor *localInlineDesc(unsigned Offset) const { + return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset); + } + private: /// Reference to the interpreter state. InterpState &S; /// Reference to the function being executed. - Function *Func; + const Function *Func; /// Current object pointer for methods. Pointer This; + /// Pointer the non-primitive return value gets constructed in. + Pointer RVOPtr; /// Return address. CodePtr RetPC; /// The size of all the arguments. diff --git a/clang/lib/AST/Interp/InterpStack.cpp b/clang/lib/AST/Interp/InterpStack.cpp index 5c803f3d9424..7fe678e62192 100644 --- a/clang/lib/AST/Interp/InterpStack.cpp +++ b/clang/lib/AST/Interp/InterpStack.cpp @@ -46,7 +46,7 @@ void *InterpStack::grow(size_t Size) { return Object; } -void *InterpStack::peek(size_t Size) { +void *InterpStack::peek(size_t Size) const { assert(Chunk && "Stack is empty!"); StackChunk *Ptr = Chunk; diff --git a/clang/lib/AST/Interp/InterpStack.h b/clang/lib/AST/Interp/InterpStack.h index b02d3c6a34b0..3adaad96515e 100644 --- a/clang/lib/AST/Interp/InterpStack.h +++ b/clang/lib/AST/Interp/InterpStack.h @@ -13,7 +13,9 @@ #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H +#include "PrimType.h" #include <memory> +#include <vector> namespace clang { namespace interp { @@ -29,10 +31,18 @@ public: /// Constructs a value in place on the top of the stack. template <typename T, typename... Tys> void push(Tys &&... Args) { new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...); +#ifndef NDEBUG + ItemTypes.push_back(toPrimType<T>()); +#endif } /// Returns the value from the top of the stack and removes it. template <typename T> T pop() { +#ifndef NDEBUG + assert(!ItemTypes.empty()); + assert(ItemTypes.back() == toPrimType<T>()); + ItemTypes.pop_back(); +#endif auto *Ptr = &peek<T>(); auto Value = std::move(*Ptr); Ptr->~T(); @@ -42,18 +52,22 @@ public: /// Discards the top value from the stack. template <typename T> void discard() { +#ifndef NDEBUG + assert(ItemTypes.back() == toPrimType<T>()); + ItemTypes.pop_back(); +#endif auto *Ptr = &peek<T>(); Ptr->~T(); shrink(aligned_size<T>()); } /// Returns a reference to the value on the top of the stack. - template <typename T> T &peek() { + template <typename T> T &peek() const { return *reinterpret_cast<T *>(peek(aligned_size<T>())); } /// Returns a pointer to the top object. - void *top() { return Chunk ? peek(0) : nullptr; } + void *top() const { return Chunk ? peek(0) : nullptr; } /// Returns the size of the stack in bytes. size_t size() const { return StackSize; } @@ -61,6 +75,9 @@ public: /// Clears the stack without calling any destructors. void clear(); + // Returns whether the stack is empty. + bool empty() const { return StackSize == 0; } + private: /// All stack slots are aligned to the native pointer alignment for storage. /// The size of an object is rounded up to a pointer alignment multiple. @@ -72,7 +89,7 @@ private: /// Grows the stack to accommodate a value and returns a pointer to it. void *grow(size_t Size); /// Returns a pointer from the top of the stack. - void *peek(size_t Size); + void *peek(size_t Size) const; /// Shrinks the stack. void shrink(size_t Size); @@ -94,10 +111,13 @@ private: : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {} /// Returns the size of the chunk, minus the header. - size_t size() { return End - start(); } + size_t size() const { return End - start(); } /// Returns a pointer to the start of the data region. char *start() { return reinterpret_cast<char *>(this + 1); } + const char *start() const { + return reinterpret_cast<const char *>(this + 1); + } }; static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); @@ -105,6 +125,45 @@ private: StackChunk *Chunk = nullptr; /// Total size of the stack. size_t StackSize = 0; + +#ifndef NDEBUG + /// vector recording the type of data we pushed into the stack. + std::vector<PrimType> ItemTypes; + + template <typename T> static constexpr PrimType toPrimType() { + if constexpr (std::is_same_v<T, Pointer>) + return PT_Ptr; + else if constexpr (std::is_same_v<T, bool> || + std::is_same_v<T, Boolean>) + return PT_Bool; + else if constexpr (std::is_same_v<T, int8_t> || + std::is_same_v<T, Integral<8, true>>) + return PT_Sint8; + else if constexpr (std::is_same_v<T, uint8_t> || + std::is_same_v<T, Integral<8, false>>) + return PT_Uint8; + else if constexpr (std::is_same_v<T, int16_t> || + std::is_same_v<T, Integral<16, true>>) + return PT_Sint16; + else if constexpr (std::is_same_v<T, uint16_t> || + std::is_same_v<T, Integral<16, false>>) + return PT_Uint16; + else if constexpr (std::is_same_v<T, int32_t> || + std::is_same_v<T, Integral<32, true>>) + return PT_Sint32; + else if constexpr (std::is_same_v<T, uint32_t> || + std::is_same_v<T, Integral<32, false>>) + return PT_Uint32; + else if constexpr (std::is_same_v<T, int64_t> || + std::is_same_v<T, Integral<64, true>>) + return PT_Sint64; + else if constexpr (std::is_same_v<T, uint64_t> || + std::is_same_v<T, Integral<64, false>>) + return PT_Uint64; + + llvm_unreachable("unknown type push()'ed into InterpStack"); + } +#endif }; } // namespace interp diff --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h index 57e36c4c63ea..033080637385 100644 --- a/clang/lib/AST/Interp/InterpState.h +++ b/clang/lib/AST/Interp/InterpState.h @@ -65,6 +65,7 @@ public: bool noteUndefinedBehavior() override { return Parent.noteUndefinedBehavior(); } + bool inConstantContext() const { return Parent.InConstantContext; } bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); } void setActiveDiagnostic(bool Flag) override { Parent.setActiveDiagnostic(Flag); @@ -81,7 +82,7 @@ public: void deallocate(Block *B); /// Delegates source mapping to the mapper. - SourceInfo getSource(Function *F, CodePtr PC) const override { + SourceInfo getSource(const Function *F, CodePtr PC) const override { return M ? M->getSource(F, PC) : F->getSource(PC); } diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 638d5b3d2357..058475b2d399 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -42,18 +42,8 @@ def ArgSint64 : ArgType { let Name = "int64_t"; } def ArgUint64 : ArgType { let Name = "uint64_t"; } def ArgBool : ArgType { let Name = "bool"; } -def ArgFunction : ArgType { let Name = "Function *"; } -def ArgRecord : ArgType { let Name = "Record *"; } - -def ArgSema : ArgType { let Name = "const fltSemantics *"; } - -def ArgExpr : ArgType { let Name = "const Expr *"; } -def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; } -def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; } -def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; } +def ArgFunction : ArgType { let Name = "const Function *"; } def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; } -def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; } -def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; } def ArgRecordField : ArgType { let Name = "const Record::Field *"; } //===----------------------------------------------------------------------===// @@ -64,15 +54,28 @@ class TypeClass { list<Type> Types; } -def AluTypeClass : TypeClass { +def NumberTypeClass : TypeClass { + let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, + Uint32, Sint64, Uint64]; +} + +def IntegerTypeClass : TypeClass { let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, - Uint32, Sint64, Uint64, Bool]; + Uint32, Sint64, Uint64]; +} + +def AluTypeClass : TypeClass { + let Types = !listconcat(NumberTypeClass.Types, [Bool]); } def PtrTypeClass : TypeClass { let Types = [Ptr]; } +def BoolTypeClass : TypeClass { + let Types = [Bool]; +} + def AllTypeClass : TypeClass { let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types); } @@ -105,6 +108,11 @@ class AluOpcode : Opcode { let HasGroup = 1; } +class IntegerOpcode : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===----------------------------------------------------------------------===// // Jump opcodes //===----------------------------------------------------------------------===// @@ -149,6 +157,13 @@ def RetValue : Opcode { // [] -> EXIT def NoRet : Opcode {} + +def Call : Opcode { + let Args = [ArgFunction]; + let Types = []; + let ChangesPC = 1; +} + //===----------------------------------------------------------------------===// // Frame management //===----------------------------------------------------------------------===// @@ -183,6 +198,7 @@ def ConstBool : ConstOpcode<Bool, ArgBool>; // [] -> [Integer] def Zero : Opcode { let Types = [AluTypeClass]; + let HasGroup = 1; } // [] -> [Pointer] @@ -253,6 +269,9 @@ def GetPtrThisVirtBase : Opcode { // [] -> [Pointer] def This : Opcode; +// [] -> [Pointer] +def RVOPtr : Opcode; + // [Pointer] -> [Pointer] def NarrowPtr : Opcode; // [Pointer] -> [Pointer] @@ -374,6 +393,12 @@ def AddOffset : AluOpcode; // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; +// Pointer, Pointer] - [Integral] +def SubPtr : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===----------------------------------------------------------------------===// // Binary operators. //===----------------------------------------------------------------------===// @@ -382,6 +407,73 @@ def SubOffset : AluOpcode; def Sub : AluOpcode; def Add : AluOpcode; def Mul : AluOpcode; +def Rem : Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} + +def Shl : Opcode { + let Types = [IntegerTypeClass, IntegerTypeClass]; + let HasGroup = 1; +} + +def Shr : Opcode { + let Types = [IntegerTypeClass, IntegerTypeClass]; + let HasGroup = 1; +} + +def BitAnd : IntegerOpcode; +def BitOr : IntegerOpcode; +def Div : Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} +def BitXor : IntegerOpcode; + +//===----------------------------------------------------------------------===// +// Unary operators. +//===----------------------------------------------------------------------===// + +// [Real] -> [Real] +def Inv: Opcode { + let Types = [BoolTypeClass]; + let HasGroup = 1; +} + +def Inc: IntegerOpcode; +def IncPop : IntegerOpcode; +def Dec: IntegerOpcode; +def DecPop: IntegerOpcode; + +// [Real] -> [Real] +def Neg: Opcode { + let Types = [AluTypeClass]; + let HasGroup = 1; +} + +// [Real] -> [Real] +def Comp: Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} + +//===----------------------------------------------------------------------===// +// Cast. +//===----------------------------------------------------------------------===// +// TODO: Expand this to handle casts between more types. + +def FromCastTypeClass : TypeClass { + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; +} + +def ToCastTypeClass : TypeClass { + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; +} + +def Cast: Opcode { + let Types = [FromCastTypeClass, ToCastTypeClass]; + let HasGroup = 1; +} //===----------------------------------------------------------------------===// // Comparison opcodes. diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index ef2638e2a36b..fd8c98fae039 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -16,6 +16,9 @@ using namespace clang::interp; Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} +Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset) + : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} + Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} Pointer::Pointer(Pointer &&P) @@ -106,7 +109,7 @@ APValue Pointer::toAPValue() const { // Build the path into the object. Pointer Ptr = *this; - while (Ptr.isField()) { + while (Ptr.isField() || Ptr.isArrayElement()) { if (Ptr.isArrayElement()) { Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex())); Ptr = Ptr.getArray(); @@ -129,14 +132,21 @@ APValue Pointer::toAPValue() const { } } + // We assemble the LValuePath starting from the innermost pointer to the + // outermost one. SO in a.b.c, the first element in Path will refer to + // the field 'c', while later code expects it to refer to 'a'. + // Just invert the order of the elements. + std::reverse(Path.begin(), Path.end()); + return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr); } bool Pointer::isInitialized() const { assert(Pointee && "Cannot check if null pointer was initialized"); Descriptor *Desc = getFieldDesc(); + assert(Desc); if (Desc->isPrimitiveArray()) { - if (Pointee->IsStatic) + if (isStatic() && Base == 0) return true; // Primitive array field are stored in a bitset. InitMap *Map = getInitMap(); @@ -154,8 +164,14 @@ bool Pointer::isInitialized() const { void Pointer::initialize() const { assert(Pointee && "Cannot initialize null pointer"); Descriptor *Desc = getFieldDesc(); - if (Desc->isPrimitiveArray()) { - if (!Pointee->IsStatic) { + + assert(Desc); + if (Desc->isArray()) { + if (Desc->isPrimitiveArray()) { + // Primitive global arrays don't have an initmap. + if (isStatic() && Base == 0) + return; + // Primitive array initializer. InitMap *&Map = getInitMap(); if (Map == (InitMap *)-1) @@ -189,5 +205,5 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { } bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { - return A.Base == B.Base && A.getFieldDesc()->IsArray; + return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h index 587531aec82a..1462d01c2412 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -33,14 +33,40 @@ enum PrimType : unsigned; /// /// This object can be allocated into interpreter stack frames. If pointing to /// a live block, it is a link in the chain of pointers pointing to the block. +/// +/// In the simplest form, a Pointer has a Block* (the pointee) and both Base +/// and Offset are 0, which means it will point to raw data. +/// +/// The Base field is used to access metadata about the data. For primitive +/// arrays, the Base is followed by an InitMap. In a variety of cases, the +/// Base is preceded by an InlineDescriptor, which is used to track the +/// initialization state, among other things. +/// +/// The Offset field is used to access the actual data. In other words, the +/// data the pointer decribes can be found at +/// Pointee->rawData() + Pointer.Offset. +/// +/// +/// Pointee Offset +/// │ │ +/// │ │ +/// ▼ ▼ +/// ┌───────┬────────────┬─────────┬────────────────────────────┐ +/// │ Block │ InlineDesc │ InitMap │ Actual Data │ +/// └───────┴────────────┴─────────┴────────────────────────────┘ +/// ▲ +/// │ +/// │ +/// Base class Pointer { private: - static constexpr unsigned PastEndMark = (unsigned)-1; - static constexpr unsigned RootPtrMark = (unsigned)-1; + static constexpr unsigned PastEndMark = ~0u; + static constexpr unsigned RootPtrMark = ~0u; public: Pointer() {} Pointer(Block *B); + Pointer(Block *B, unsigned BaseAndOffset); Pointer(const Pointer &P); Pointer(Pointer &&P); ~Pointer(); @@ -216,6 +242,8 @@ public: /// Returns the record descriptor of a class. Record *getRecord() const { return getFieldDesc()->ElemRecord; } + // Returns the element record type, if this is a non-primive array. + Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; } /// Returns the field information. const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } @@ -232,7 +260,9 @@ public: bool isStaticTemporary() const { return isStatic() && isTemporary(); } /// Checks if the field is mutable. - bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; } + bool isMutable() const { + return Base != 0 && getInlineDesc()->IsFieldMutable; + } /// Checks if an object was initialized. bool isInitialized() const; /// Checks if the object is active. @@ -246,7 +276,7 @@ public: } /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } + std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } /// Returns the byte offset from the start. unsigned getByteOffset() const { @@ -276,12 +306,12 @@ public: /// Dereferences the pointer, if it's live. template <typename T> T &deref() const { assert(isLive() && "Invalid pointer"); - return *reinterpret_cast<T *>(Pointee->data() + Offset); + return *reinterpret_cast<T *>(Pointee->rawData() + Offset); } /// Dereferences a primitive element. template <typename T> T &elem(unsigned I) const { - return reinterpret_cast<T *>(Pointee->data())[I]; + return reinterpret_cast<T *>(Pointee->rawData())[I]; } /// Initializes a field. @@ -298,7 +328,7 @@ public: /// Prints the pointer. void print(llvm::raw_ostream &OS) const { - OS << "{" << Base << ", " << Offset << ", "; + OS << Pointee << " {" << Base << ", " << Offset << ", "; if (Pointee) OS << Pointee->getSize(); else @@ -318,12 +348,13 @@ private: /// Returns a descriptor at a given offset. InlineDescriptor *getDescriptor(unsigned Offset) const { assert(Offset != 0 && "Not a nested pointer"); - return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1; + return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - + 1; } /// Returns a reference to the pointer which stores the initialization map. InitMap *&getInitMap() const { - return *reinterpret_cast<InitMap **>(Pointee->data() + Base); + return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base); } /// The block the pointer is pointing to. diff --git a/clang/lib/AST/Interp/PrimType.cpp b/clang/lib/AST/Interp/PrimType.cpp index 082bfaf3c207..eda90e1c36c2 100644 --- a/clang/lib/AST/Interp/PrimType.cpp +++ b/clang/lib/AST/Interp/PrimType.cpp @@ -1,4 +1,4 @@ -//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===// +//===--- PrimType.cpp - Types for the constexpr VM --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "PrimType.h" +#include "Boolean.h" +#include "Pointer.h" using namespace clang; using namespace clang::interp; diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h index de4bf9bf802e..c8f2a600fb3c 100644 --- a/clang/lib/AST/Interp/PrimType.h +++ b/clang/lib/AST/Interp/PrimType.h @@ -13,16 +13,17 @@ #ifndef LLVM_CLANG_AST_INTERP_TYPE_H #define LLVM_CLANG_AST_INTERP_TYPE_H +#include "Integral.h" #include <climits> #include <cstddef> #include <cstdint> -#include "Boolean.h" -#include "Integral.h" -#include "Pointer.h" namespace clang { namespace interp { +class Pointer; +class Boolean; + /// Enumeration of the primitive types of the VM. enum PrimType : unsigned { PT_Sint8, @@ -58,6 +59,13 @@ constexpr size_t align(size_t Size) { return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *); } +constexpr bool aligned(uintptr_t Value) { return Value == align(Value); } +static_assert(aligned(sizeof(void *))); + +static inline bool aligned(const void *P) { + return aligned(reinterpret_cast<uintptr_t>(P)); +} + inline bool isPrimitiveIntegral(PrimType Type) { switch (Type) { case PT_Bool: diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp index e310c9678140..5305ddd8de18 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -53,10 +53,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) { } // Create a descriptor for the string. - Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, - /*isConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); + Descriptor *Desc = + allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1, + /*isConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false); // Allocate storage for the string. // The byte length does not include the null terminator. @@ -64,6 +65,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) { unsigned Sz = Desc->getAllocSize(); auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, /*isExtern=*/false); + G->block()->invokeCtor(); Globals.push_back(G); // Construct the string in storage. @@ -99,13 +101,13 @@ Pointer Program::getPtrGlobal(unsigned Idx) { return Pointer(Globals[Idx]->block()); } -llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { auto It = GlobalIndices.find(VD); if (It != GlobalIndices.end()) return It->second; // Find any previous declarations which were already evaluated. - llvm::Optional<unsigned> Index; + std::optional<unsigned> Index; for (const Decl *P = VD; P; P = P->getPreviousDecl()) { auto It = GlobalIndices.find(P); if (It != GlobalIndices.end()) { @@ -123,18 +125,19 @@ llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { return Index; } -llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init) { if (auto Idx = getGlobal(VD)) return Idx; - if (auto Idx = createGlobal(VD)) { + if (auto Idx = createGlobal(VD, Init)) { GlobalIndices[VD] = *Idx; return Idx; } return {}; } -llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { +std::optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { auto &ASTCtx = Ctx.getASTContext(); // Create a pointer to an incomplete array of the specified elements. @@ -153,7 +156,9 @@ llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { return {}; } -llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, + const Expr *Init) { + assert(!getGlobal(VD)); bool IsStatic, IsExtern; if (auto *Var = dyn_cast<VarDecl>(VD)) { IsStatic = !Var->hasLocalStorage(); @@ -162,7 +167,7 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { IsStatic = false; IsExtern = true; } - if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) { + if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) { for (const Decl *P = VD; P; P = P->getPreviousDecl()) GlobalIndices[P] = *Idx; return *Idx; @@ -170,20 +175,22 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { return {}; } -llvm::Optional<unsigned> Program::createGlobal(const Expr *E) { +std::optional<unsigned> Program::createGlobal(const Expr *E) { return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false); } -llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern) { +std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, + const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); const bool IsTemporary = D.dyn_cast<const Expr *>(); if (auto T = Ctx.classify(Ty)) { - Desc = createDescriptor(D, *T, IsConst, IsTemporary); + Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary); } else { - Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); + Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst, + IsTemporary); } if (!Desc) return {}; @@ -201,24 +208,12 @@ llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, } Function *Program::getFunction(const FunctionDecl *F) { - F = F->getDefinition(); + F = F->getCanonicalDecl(); + assert(F); auto It = Funcs.find(F); return It == Funcs.end() ? nullptr : It->second.get(); } -llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) { - if (Function *Func = getFunction(F)) { - return Func; - } - - // Try to compile the function if it wasn't compiled yet. - if (const FunctionDecl *FD = F->getDefinition()) - return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD); - - // A relocation which traps if not resolved. - return nullptr; -} - Record *Program::getOrCreateRecord(const RecordDecl *RD) { // Use the actual definition as a key. RD = RD->getDefinition(); @@ -231,8 +226,13 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { return It->second; } + // We insert nullptr now and replace that later, so recursive calls + // to this function with the same RecordDecl don't run into + // infinite recursion. + Records.insert({RD, nullptr}); + // Number of bytes required by fields and base classes. - unsigned Size = 0; + unsigned BaseSize = 0; // Number of bytes required by virtual base. unsigned VirtSize = 0; @@ -240,7 +240,7 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { if (!BR) return nullptr; - return allocateDescriptor(BD, BR, /*isConst=*/false, + return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false, /*isTemporary=*/false, /*isMutable=*/false); }; @@ -256,9 +256,9 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl(); Record *BR = getOrCreateRecord(BD); if (Descriptor *Desc = GetBaseDesc(BD, BR)) { - Size += align(sizeof(InlineDescriptor)); - Bases.push_back({BD, Size, Desc, BR}); - Size += align(BR->getSize()); + BaseSize += align(sizeof(InlineDescriptor)); + Bases.push_back({BD, BaseSize, Desc, BR}); + BaseSize += align(BR->getSize()); continue; } return nullptr; @@ -282,39 +282,41 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { Record::FieldList Fields; for (const FieldDecl *FD : RD->fields()) { // Reserve space for the field's descriptor and the offset. - Size += align(sizeof(InlineDescriptor)); + BaseSize += align(sizeof(InlineDescriptor)); // Classify the field and add its metadata. QualType FT = FD->getType(); const bool IsConst = FT.isConstQualified(); const bool IsMutable = FD->isMutable(); Descriptor *Desc; - if (llvm::Optional<PrimType> T = Ctx.classify(FT)) { - Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, - IsMutable); + if (std::optional<PrimType> T = Ctx.classify(FT)) { + Desc = createDescriptor(FD, *T, std::nullopt, IsConst, + /*isTemporary=*/false, IsMutable); } else { - Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, + Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst, /*isTemporary=*/false, IsMutable); } if (!Desc) return nullptr; - Fields.push_back({FD, Size, Desc}); - Size += align(Desc->getAllocSize()); + Fields.push_back({FD, BaseSize, Desc}); + BaseSize += align(Desc->getAllocSize()); } Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields), - std::move(VirtBases), VirtSize, Size); - Records.insert({RD, R}); + std::move(VirtBases), VirtSize, BaseSize); + Records[RD] = R; return R; } Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize, bool IsConst, bool IsTemporary, - bool IsMutable) { + bool IsMutable, const Expr *Init) { // Classes and structures. if (auto *RT = Ty->getAs<RecordType>()) { if (auto *Record = getOrCreateRecord(RT->getDecl())) - return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary, + IsMutable); } // Arrays. @@ -323,38 +325,39 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // Array of well-known bounds. if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) { size_t NumElems = CAT->getSize().getZExtValue(); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { // Arrays of primitives. unsigned ElemSize = primSize(*T); if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) { return {}; } - return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, + return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary, IsMutable); } else { // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); - if (!Desc) + Descriptor *ElemDesc = createDescriptor( + D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary); + if (!ElemDesc) return nullptr; - InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); + InterpSize ElemSize = + ElemDesc->getAllocSize() + sizeof(InlineDescriptor); if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) return {}; - return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, - IsMutable); + return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst, + IsTemporary, IsMutable); } } // Array of unknown bounds - cannot be accessed and pointer arithmetic // is forbidden on pointers to such objects. if (isa<IncompleteArrayType>(ArrayType)) { - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { return allocateDescriptor(D, *T, IsTemporary, Descriptor::UnknownSize{}); } else { - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize, + IsConst, IsTemporary); if (!Desc) return nullptr; return allocateDescriptor(D, Desc, IsTemporary, @@ -366,13 +369,15 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // Atomic types. if (auto *AT = Ty->getAs<AtomicType>()) { const Type *InnerTy = AT->getValueType().getTypePtr(); - return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); + return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary, + IsMutable); } // Complex types - represented as arrays of elements. if (auto *CT = Ty->getAs<ComplexType>()) { PrimType ElemTy = *Ctx.classify(CT->getElementType()); - return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary, + IsMutable); } return nullptr; diff --git a/clang/lib/AST/Interp/Program.h b/clang/lib/AST/Interp/Program.h index ca985af8ad30..5a80dd1ed748 100644 --- a/clang/lib/AST/Interp/Program.h +++ b/clang/lib/AST/Interp/Program.h @@ -37,10 +37,26 @@ class Context; class Record; /// The program contains and links the bytecode for all functions. -class Program { +class Program final { public: Program(Context &Ctx) : Ctx(Ctx) {} + ~Program() { + // Manually destroy all the blocks. They are almost all harmless, + // but primitive arrays might have an InitMap* heap allocated and + // that needs to be freed. + for (Global *G : Globals) + G->block()->invokeDtor(); + + // Records might actually allocate memory themselves, but they + // are allocated using a BumpPtrAllocator. Call their desctructors + // here manually so they are properly freeing their resources. + for (auto RecordPair : Records) { + if (Record *R = RecordPair.second) + R->~Record(); + } + } + /// Marshals a native pointer to an ID for embedding in bytecode. unsigned getOrCreateNativePointer(const void *Ptr); @@ -60,23 +76,25 @@ public: } /// Finds a global's index. - llvm::Optional<unsigned> getGlobal(const ValueDecl *VD); + std::optional<unsigned> getGlobal(const ValueDecl *VD); /// Returns or creates a global an creates an index to it. - llvm::Optional<unsigned> getOrCreateGlobal(const ValueDecl *VD); + std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init = nullptr); /// Returns or creates a dummy value for parameters. - llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD); + std::optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD); /// Creates a global and returns its index. - llvm::Optional<unsigned> createGlobal(const ValueDecl *VD); + std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *E); /// Creates a global from a lifetime-extended temporary. - llvm::Optional<unsigned> createGlobal(const Expr *E); + std::optional<unsigned> createGlobal(const Expr *E); /// Creates a new function from a code range. template <typename... Ts> Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { + Def = Def->getCanonicalDecl(); auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...); Funcs.insert({Def, std::unique_ptr<Function>(Func)}); return Func; @@ -92,26 +110,23 @@ public: /// Returns a function. Function *getFunction(const FunctionDecl *F); - /// Returns a pointer to a function if it exists and can be compiled. - /// If a function couldn't be compiled, an error is returned. - /// If a function was not yet defined, a null pointer is returned. - llvm::Expected<Function *> getOrCreateFunction(const FunctionDecl *F); - /// Returns a record or creates one if it does not exist. Record *getOrCreateRecord(const RecordDecl *RD); /// Creates a descriptor for a primitive type. Descriptor *createDescriptor(const DeclTy &D, PrimType Type, - bool IsConst = false, - bool IsTemporary = false, + Descriptor::MetadataSize MDSize = std::nullopt, + bool IsConst = false, bool IsTemporary = false, bool IsMutable = false) { - return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable); } /// Creates a descriptor for a composite type. Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize = std::nullopt, bool IsConst = false, bool IsTemporary = false, - bool IsMutable = false); + bool IsMutable = false, + const Expr *Init = nullptr); /// Context to manage declaration lifetimes. class DeclScope { @@ -124,17 +139,18 @@ public: }; /// Returns the current declaration ID. - llvm::Optional<unsigned> getCurrentDecl() const { + std::optional<unsigned> getCurrentDecl() const { if (CurrentDeclaration == NoDeclaration) - return llvm::Optional<unsigned>{}; + return std::optional<unsigned>{}; return LastDeclaration; } private: friend class DeclScope; - llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern); + std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, + const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h index 9cdee9003752..1742cb1cc4ee 100644 --- a/clang/lib/AST/Interp/Record.h +++ b/clang/lib/AST/Interp/Record.h @@ -13,14 +13,16 @@ #ifndef LLVM_CLANG_AST_INTERP_RECORD_H #define LLVM_CLANG_AST_INTERP_RECORD_H -#include "Pointer.h" +#include "Descriptor.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" namespace clang { namespace interp { class Program; /// Structure/Class descriptor. -class Record { +class Record final { public: /// Describes a record field. struct Field { @@ -47,6 +49,8 @@ public: public: /// Returns the underlying declaration. const RecordDecl *getDecl() const { return Decl; } + /// Returns the name of the underlying declaration. + const std::string getName() const { return Decl->getNameAsString(); } /// Checks if the record is a union. bool isUnion() const { return getDecl()->isUnion(); } /// Returns the size of the record. @@ -59,13 +63,20 @@ public: const Base *getBase(const RecordDecl *FD) const; /// Returns a virtual base descriptor. const Base *getVirtualBase(const RecordDecl *RD) const; + // Returns the destructor of the record, if any. + const CXXDestructorDecl *getDestructor() const { + if (const auto *CXXDecl = dyn_cast<CXXRecordDecl>(Decl)) + return CXXDecl->getDestructor(); + return nullptr; + } using const_field_iter = FieldList::const_iterator; llvm::iterator_range<const_field_iter> fields() const { return llvm::make_range(Fields.begin(), Fields.end()); } - unsigned getNumFields() { return Fields.size(); } + unsigned getNumFields() const { return Fields.size(); } + const Field *getField(unsigned I) const { return &Fields[I]; } Field *getField(unsigned I) { return &Fields[I]; } using const_base_iter = BaseList::const_iterator; @@ -73,7 +84,7 @@ public: return llvm::make_range(Bases.begin(), Bases.end()); } - unsigned getNumBases() { return Bases.size(); } + unsigned getNumBases() const { return Bases.size(); } Base *getBase(unsigned I) { return &Bases[I]; } using const_virtual_iter = VirtualBaseList::const_iterator; @@ -81,7 +92,7 @@ public: return llvm::make_range(VirtualBases.begin(), VirtualBases.end()); } - unsigned getNumVirtualBases() { return VirtualBases.size(); } + unsigned getNumVirtualBases() const { return VirtualBases.size(); } Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; } private: @@ -108,7 +119,6 @@ private: llvm::DenseMap<const FieldDecl *, Field *> FieldMap; /// Mapping from declarations to virtual bases. llvm::DenseMap<const RecordDecl *, Base *> VirtualBaseMap; - /// Mapping from /// Size of the structure. unsigned BaseSize; /// Size of all virtual bases. diff --git a/clang/lib/AST/Interp/Source.cpp b/clang/lib/AST/Interp/Source.cpp index 4bec87812638..467cde116843 100644 --- a/clang/lib/AST/Interp/Source.cpp +++ b/clang/lib/AST/Interp/Source.cpp @@ -28,12 +28,12 @@ const Expr *SourceInfo::asExpr() const { return nullptr; } -const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const { +const Expr *SourceMapper::getExpr(const Function *F, CodePtr PC) const { if (const Expr *E = getSource(F, PC).asExpr()) return E; llvm::report_fatal_error("missing source expression"); } -SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const { +SourceLocation SourceMapper::getLocation(const Function *F, CodePtr PC) const { return getSource(F, PC).getLoc(); } diff --git a/clang/lib/AST/Interp/Source.h b/clang/lib/AST/Interp/Source.h index 6acaf406b47a..99ffce34c12f 100644 --- a/clang/lib/AST/Interp/Source.h +++ b/clang/lib/AST/Interp/Source.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_SOURCE_H #define LLVM_CLANG_AST_INTERP_SOURCE_H +#include "PrimType.h" #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "llvm/Support/Endian.h" @@ -22,7 +23,7 @@ namespace interp { class Function; /// Pointer into the code segment. -class CodePtr { +class CodePtr final { public: CodePtr() : Ptr(nullptr) {} @@ -43,11 +44,14 @@ public: bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; } + operator bool() const { return Ptr; } + /// Reads data and advances the pointer. template <typename T> std::enable_if_t<!std::is_pointer<T>::value, T> read() { + assert(aligned(Ptr)); using namespace llvm::support; T Value = endian::read<T, endianness::native, 1>(Ptr); - Ptr += sizeof(T); + Ptr += align(sizeof(T)); return Value; } @@ -63,7 +67,7 @@ private: }; /// Describes the statement/declaration an opcode was generated from. -class SourceInfo { +class SourceInfo final { public: SourceInfo() {} SourceInfo(const Stmt *E) : Source(E) {} @@ -89,12 +93,12 @@ public: virtual ~SourceMapper() {} /// Returns source information for a given PC in a function. - virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0; + virtual SourceInfo getSource(const Function *F, CodePtr PC) const = 0; /// Returns the expression if an opcode belongs to one, null otherwise. - const Expr *getExpr(Function *F, CodePtr PC) const; + const Expr *getExpr(const Function *F, CodePtr PC) const; /// Returns the location from which an opcode originates. - SourceLocation getLocation(Function *F, CodePtr PC) const; + SourceLocation getLocation(const Function *F, CodePtr PC) const; }; } // namespace interp diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h index d9a645a3eb3e..131fbcf3cffc 100644 --- a/clang/lib/AST/Interp/State.h +++ b/clang/lib/AST/Interp/State.h @@ -71,6 +71,7 @@ public: virtual unsigned getCallStackDepth() = 0; public: + State() : InConstantContext(false) {} // Diagnose that the evaluation could not be folded (FF => FoldFailure) OptionalDiagnostic FFDiag(SourceLocation Loc, @@ -118,6 +119,10 @@ public: const LangOptions &getLangOpts() const; + /// Whether or not we're in a context where the front end requires a + /// constant value. + bool InConstantContext; + private: void addCallStack(unsigned Limit); diff --git a/clang/lib/AST/ItaniumCXXABI.cpp b/clang/lib/AST/ItaniumCXXABI.cpp index e99c21dcff73..c9aadce73141 100644 --- a/clang/lib/AST/ItaniumCXXABI.cpp +++ b/clang/lib/AST/ItaniumCXXABI.cpp @@ -26,6 +26,7 @@ #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/iterator.h" +#include <optional> using namespace clang; @@ -84,8 +85,8 @@ template<typename T> bool isDenseMapKeyTombstone(T V) { V, llvm::DenseMapInfo<T>::getTombstoneKey()); } -template<typename T> -Optional<bool> areDenseMapKeysEqualSpecialValues(T LHS, T RHS) { +template <typename T> +std::optional<bool> areDenseMapKeysEqualSpecialValues(T LHS, T RHS) { bool LHSEmpty = isDenseMapKeyEmpty(LHS); bool RHSEmpty = isDenseMapKeyEmpty(RHS); if (LHSEmpty || RHSEmpty) @@ -96,7 +97,7 @@ Optional<bool> areDenseMapKeysEqualSpecialValues(T LHS, T RHS) { if (LHSTombstone || RHSTombstone) return LHSTombstone && RHSTombstone; - return None; + return std::nullopt; } template<> @@ -113,8 +114,8 @@ struct DenseMapInfo<DecompositionDeclName> { return llvm::hash_combine_range(Key.begin(), Key.end()); } static bool isEqual(DecompositionDeclName LHS, DecompositionDeclName RHS) { - if (Optional<bool> Result = areDenseMapKeysEqualSpecialValues( - LHS.Bindings, RHS.Bindings)) + if (std::optional<bool> Result = + areDenseMapKeysEqualSpecialValues(LHS.Bindings, RHS.Bindings)) return *Result; return LHS.Bindings.size() == RHS.Bindings.size() && @@ -224,7 +225,7 @@ public: MemberPointerInfo getMemberPointerInfo(const MemberPointerType *MPT) const override { const TargetInfo &Target = Context.getTargetInfo(); - TargetInfo::IntType PtrDiff = Target.getPtrDiffType(0); + TargetInfo::IntType PtrDiff = Target.getPtrDiffType(LangAS::Default); MemberPointerInfo MPI; MPI.Width = Target.getTypeWidth(PtrDiff); MPI.Align = Target.getTypeAlign(PtrDiff); @@ -251,8 +252,8 @@ public: return false; const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); - CharUnits PointerSize = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); + CharUnits PointerSize = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerWidth(LangAS::Default)); return Layout.getNonVirtualSize() == PointerSize; } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 91f41778ee68..b23bc5f8d881 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -35,6 +35,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; @@ -118,9 +119,9 @@ public: void mangleDynamicAtExitDestructor(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &Out) override; - void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, + void mangleSEHFilterExpression(GlobalDecl EnclosingDecl, raw_ostream &Out) override; - void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl, + void mangleSEHFinallyBlock(GlobalDecl EnclosingDecl, raw_ostream &Out) override; void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &) override; void mangleItaniumThreadLocalWrapper(const VarDecl *D, @@ -484,8 +485,7 @@ private: const AbiTagList *AdditionalAbiTags); void mangleModuleName(const NamedDecl *ND); void mangleTemplateName(const TemplateDecl *TD, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs); + ArrayRef<TemplateArgument> Args); void mangleUnqualifiedName(GlobalDecl GD, const DeclContext *DC, const AbiTagList *AdditionalAbiTags) { mangleUnqualifiedName(GD, cast<NamedDecl>(GD.getDecl())->getDeclName(), DC, @@ -513,8 +513,7 @@ private: const AbiTagList *AdditionalAbiTags, bool NoFunction=false); void mangleNestedName(const TemplateDecl *TD, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs); + ArrayRef<TemplateArgument> Args); void mangleNestedNameWithClosurePrefix(GlobalDecl GD, const NamedDecl *PrefixND, const AbiTagList *AdditionalAbiTags); @@ -578,8 +577,7 @@ private: void mangleTemplateArgs(TemplateName TN, const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs); - void mangleTemplateArgs(TemplateName TN, const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs); + void mangleTemplateArgs(TemplateName TN, ArrayRef<TemplateArgument> Args); void mangleTemplateArgs(TemplateName TN, const TemplateArgumentList &AL); void mangleTemplateArg(TemplateArgument A, bool NeedExactType); void mangleTemplateArgExpr(const Expr *E); @@ -605,9 +603,9 @@ NamespaceDecl *ItaniumMangleContextImpl::getStdNamespace() { if (!StdNamespace) { StdNamespace = NamespaceDecl::Create( getASTContext(), getASTContext().getTranslationUnitDecl(), - /*Inline*/ false, SourceLocation(), SourceLocation(), + /*Inline=*/false, SourceLocation(), SourceLocation(), &getASTContext().Idents.get("std"), - /*PrevDecl*/ nullptr); + /*PrevDecl=*/nullptr, /*Nested=*/false); StdNamespace->setImplicit(); } return StdNamespace; @@ -1087,15 +1085,14 @@ void CXXNameMangler::mangleModuleNamePrefix(StringRef Name, bool IsPartition) { } void CXXNameMangler::mangleTemplateName(const TemplateDecl *TD, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs) { + ArrayRef<TemplateArgument> Args) { const DeclContext *DC = Context.getEffectiveDeclContext(TD); if (DC->isTranslationUnit() || isStdNamespace(DC)) { mangleUnscopedTemplateName(TD, DC, nullptr); - mangleTemplateArgs(asTemplateName(TD), TemplateArgs, NumTemplateArgs); + mangleTemplateArgs(asTemplateName(TD), Args); } else { - mangleNestedName(TD, TemplateArgs, NumTemplateArgs); + mangleNestedName(TD, Args); } } @@ -1244,8 +1241,7 @@ void CXXNameMangler::manglePrefix(QualType type) { // FIXME: GCC does not appear to mangle the template arguments when // the template in question is a dependent template name. Should we // emulate that badness? - mangleTemplateArgs(TST->getTemplateName(), TST->getArgs(), - TST->getNumArgs()); + mangleTemplateArgs(TST->getTemplateName(), TST->template_arguments()); addSubstitution(QualType(TST, 0)); } } else if (const auto *DTST = @@ -1258,7 +1254,7 @@ void CXXNameMangler::manglePrefix(QualType type) { // FIXME: GCC does not appear to mangle the template arguments when // the template in question is a dependent template name. Should we // emulate that badness? - mangleTemplateArgs(Template, DTST->getArgs(), DTST->getNumArgs()); + mangleTemplateArgs(Template, DTST->template_arguments()); addSubstitution(QualType(DTST, 0)); } } else { @@ -1558,7 +1554,7 @@ void CXXNameMangler::mangleUnqualifiedName( // <lambda-sig> ::= <template-param-decl>* <parameter-type>+ // # Parameter types or 'v' for 'void'. if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) { - llvm::Optional<unsigned> DeviceNumber = + std::optional<unsigned> DeviceNumber = Context.getDiscriminatorOverride()(Context.getASTContext(), Record); // If we have a device-number via the discriminator, use that to mangle @@ -1588,7 +1584,9 @@ void CXXNameMangler::mangleUnqualifiedName( // Get a unique id for the anonymous struct. If it is not a real output // ID doesn't matter so use fake one. - unsigned AnonStructId = NullOut ? 0 : Context.getAnonymousStructId(TD); + unsigned AnonStructId = + NullOut ? 0 + : Context.getAnonymousStructId(TD, dyn_cast<FunctionDecl>(DC)); // Mangle it as a source name in the form // [n] $_<id> @@ -1659,7 +1657,7 @@ void CXXNameMangler::mangleUnqualifiedName( if (!MD->isStatic()) Arity++; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case DeclarationName::CXXConversionFunctionName: case DeclarationName::CXXLiteralOperatorName: mangleOperatorName(Name, Arity); @@ -1730,14 +1728,13 @@ void CXXNameMangler::mangleNestedName(GlobalDecl GD, Out << 'E'; } void CXXNameMangler::mangleNestedName(const TemplateDecl *TD, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs) { + ArrayRef<TemplateArgument> Args) { // <nested-name> ::= N [<CV-qualifiers>] <template-prefix> <template-args> E Out << 'N'; mangleTemplatePrefix(TD); - mangleTemplateArgs(asTemplateName(TD), TemplateArgs, NumTemplateArgs); + mangleTemplateArgs(asTemplateName(TD), Args); Out << 'E'; } @@ -2006,7 +2003,7 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // if the host-side CXX ABI has different numbering for lambda. In such case, // if the mangle context is that device-side one, use the device-side lambda // mangling number for this lambda. - llvm::Optional<unsigned> DeviceNumber = + std::optional<unsigned> DeviceNumber = Context.getDiscriminatorOverride()(Context.getASTContext(), Lambda); unsigned Number = DeviceNumber ? *DeviceNumber : Lambda->getLambdaManglingNumber(); @@ -2416,7 +2413,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, // conversions to the corresponding template parameter. // FIXME: Other compilers mangle partially-resolved template arguments in // unresolved-qualifier-levels. - mangleTemplateArgs(TemplateName(), TST->getArgs(), TST->getNumArgs()); + mangleTemplateArgs(TemplateName(), TST->template_arguments()); break; } @@ -2435,7 +2432,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, TemplateName Template = getASTContext().getDependentTemplateName( DTST->getQualifier(), DTST->getIdentifier()); mangleSourceName(DTST->getIdentifier()); - mangleTemplateArgs(Template, DTST->getArgs(), DTST->getNumArgs()); + mangleTemplateArgs(Template, DTST->template_arguments()); break; } @@ -3874,7 +3871,7 @@ void CXXNameMangler::mangleType(const InjectedClassNameType *T) { void CXXNameMangler::mangleType(const TemplateSpecializationType *T) { if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl()) { - mangleTemplateName(TD, T->getArgs(), T->getNumArgs()); + mangleTemplateName(TD, T->template_arguments()); } else { if (mangleSubstitution(QualType(T, 0))) return; @@ -3884,7 +3881,7 @@ void CXXNameMangler::mangleType(const TemplateSpecializationType *T) { // FIXME: GCC does not appear to mangle the template arguments when // the template in question is a dependent template name. Should we // emulate that badness? - mangleTemplateArgs(T->getTemplateName(), T->getArgs(), T->getNumArgs()); + mangleTemplateArgs(T->getTemplateName(), T->template_arguments()); addSubstitution(QualType(T, 0)); } } @@ -3936,7 +3933,7 @@ void CXXNameMangler::mangleType(const DependentTemplateSpecializationType *T) { // FIXME: GCC does not appear to mangle the template arguments when // the template in question is a dependent template name. Should we // emulate that badness? - mangleTemplateArgs(Prefix, T->getArgs(), T->getNumArgs()); + mangleTemplateArgs(Prefix, T->template_arguments()); Out << 'E'; } @@ -3980,16 +3977,22 @@ void CXXNameMangler::mangleType(const UnaryTransformType *T) { // If this is dependent, we need to record that. If not, we simply // mangle it as the underlying type since they are equivalent. if (T->isDependentType()) { - Out << 'U'; + Out << "u"; + StringRef BuiltinName; switch (T->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - Out << "3eut"; - break; +#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \ + case UnaryTransformType::Enum: \ + BuiltinName = "__" #Trait; \ + break; +#include "clang/Basic/TransformTypeTraits.def" } + Out << BuiltinName.size() << BuiltinName; } + Out << "I"; mangleType(T->getBaseType()); + Out << "E"; } void CXXNameMangler::mangleType(const AutoType *T) { @@ -4247,6 +4250,7 @@ recurse: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: case Expr::CXXInheritedCtorInitExprClass: + case Expr::CXXParenListInitExprClass: llvm_unreachable("unexpected statement kind"); case Expr::ConstantExprClass: @@ -4671,7 +4675,7 @@ recurse: Out << 'E'; break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case UETT_AlignOf: Out << 'a'; MangleAlignofSizeofArg(); @@ -4886,9 +4890,7 @@ recurse: // <expr-primary> ::= L <mangled-name> E # external name Out << "L_Z"; auto *CSE = cast<ConceptSpecializationExpr>(E); - mangleTemplateName(CSE->getNamedConcept(), - CSE->getTemplateArguments().data(), - CSE->getTemplateArguments().size()); + mangleTemplateName(CSE->getNamedConcept(), CSE->getTemplateArguments()); Out << 'E'; break; } @@ -5345,13 +5347,12 @@ void CXXNameMangler::mangleTemplateArgs(TemplateName TN, } void CXXNameMangler::mangleTemplateArgs(TemplateName TN, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs) { + ArrayRef<TemplateArgument> Args) { // <template-args> ::= I <template-arg>+ E Out << 'I'; TemplateArgManglingInfo Info(TN); - for (unsigned i = 0; i != NumTemplateArgs; ++i) - mangleTemplateArg(TemplateArgs[i], Info.needExactType(i, TemplateArgs[i])); + for (unsigned i = 0; i != Args.size(); ++i) + mangleTemplateArg(Args[i], Info.needExactType(i, Args[i])); Out << 'E'; } @@ -6431,23 +6432,25 @@ void ItaniumMangleContextImpl::mangleDynamicStermFinalizer(const VarDecl *D, } void ItaniumMangleContextImpl::mangleSEHFilterExpression( - const NamedDecl *EnclosingDecl, raw_ostream &Out) { + GlobalDecl EnclosingDecl, raw_ostream &Out) { CXXNameMangler Mangler(*this, Out); Mangler.getStream() << "__filt_"; - if (shouldMangleDeclName(EnclosingDecl)) + auto *EnclosingFD = cast<FunctionDecl>(EnclosingDecl.getDecl()); + if (shouldMangleDeclName(EnclosingFD)) Mangler.mangle(EnclosingDecl); else - Mangler.getStream() << EnclosingDecl->getName(); + Mangler.getStream() << EnclosingFD->getName(); } void ItaniumMangleContextImpl::mangleSEHFinallyBlock( - const NamedDecl *EnclosingDecl, raw_ostream &Out) { + GlobalDecl EnclosingDecl, raw_ostream &Out) { CXXNameMangler Mangler(*this, Out); Mangler.getStream() << "__fin_"; - if (shouldMangleDeclName(EnclosingDecl)) + auto *EnclosingFD = cast<FunctionDecl>(EnclosingDecl.getDecl()); + if (shouldMangleDeclName(EnclosingFD)) Mangler.mangle(EnclosingDecl); else - Mangler.getStream() << EnclosingDecl->getName(); + Mangler.getStream() << EnclosingFD->getName(); } void ItaniumMangleContextImpl::mangleItaniumThreadLocalInit(const VarDecl *D, @@ -6558,8 +6561,8 @@ ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context, bool IsAux) { return new ItaniumMangleContextImpl( Context, Diags, - [](ASTContext &, const NamedDecl *) -> llvm::Optional<unsigned> { - return llvm::None; + [](ASTContext &, const NamedDecl *) -> std::optional<unsigned> { + return std::nullopt; }, IsAux); } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 87e4255c2b93..83b097daf8ab 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1,8 +1,9 @@ #include "clang/AST/JSONNodeDumper.h" +#include "clang/AST/Type.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Lex/Lexer.h" -#include "llvm/ADT/StringSwitch.h" +#include <optional> using namespace clang; @@ -530,6 +531,14 @@ JSONNodeDumper::createCXXBaseSpecifier(const CXXBaseSpecifier &BS) { void JSONNodeDumper::VisitTypedefType(const TypedefType *TT) { JOS.attribute("decl", createBareDeclRef(TT->getDecl())); + if (!TT->typeMatchesDecl()) + JOS.attribute("type", createQualType(TT->desugar())); +} + +void JSONNodeDumper::VisitUsingType(const UsingType *TT) { + JOS.attribute("decl", createBareDeclRef(TT->getFoundDecl())); + if (!TT->typeMatchesDecl()) + JOS.attribute("type", createQualType(TT->desugar())); } void JSONNodeDumper::VisitFunctionType(const FunctionType *T) { @@ -662,9 +671,11 @@ void JSONNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *UUT) { void JSONNodeDumper::VisitUnaryTransformType(const UnaryTransformType *UTT) { switch (UTT->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - JOS.attribute("transformKind", "underlying_type"); +#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \ + case UnaryTransformType::Enum: \ + JOS.attribute("transformKind", #Trait); \ break; +#include "clang/Basic/TransformTypeTraits.def" } } @@ -680,6 +691,18 @@ void JSONNodeDumper::VisitTemplateTypeParmType( JOS.attribute("decl", createBareDeclRef(TTPT->getDecl())); } +void JSONNodeDumper::VisitSubstTemplateTypeParmType( + const SubstTemplateTypeParmType *STTPT) { + JOS.attribute("index", STTPT->getIndex()); + if (auto PackIndex = STTPT->getPackIndex()) + JOS.attribute("pack_index", *PackIndex); +} + +void JSONNodeDumper::VisitSubstTemplateTypeParmPackType( + const SubstTemplateTypeParmPackType *T) { + JOS.attribute("index", T->getIndex()); +} + void JSONNodeDumper::VisitAutoType(const AutoType *AT) { JOS.attribute("undeduced", !AT->isDeduced()); switch (AT->getKeyword()) { @@ -715,7 +738,7 @@ void JSONNodeDumper::VisitObjCInterfaceType(const ObjCInterfaceType *OIT) { } void JSONNodeDumper::VisitPackExpansionType(const PackExpansionType *PET) { - if (llvm::Optional<unsigned> N = PET->getNumExpansions()) + if (std::optional<unsigned> N = PET->getNumExpansions()) JOS.attribute("numExpansions", *N); } @@ -772,6 +795,7 @@ void JSONNodeDumper::VisitTypeAliasDecl(const TypeAliasDecl *TAD) { void JSONNodeDumper::VisitNamespaceDecl(const NamespaceDecl *ND) { VisitNamedDecl(ND); attributeOnlyIfTrue("isInline", ND->isInline()); + attributeOnlyIfTrue("isNested", ND->isNested()); if (!ND->isOriginalNamespace()) JOS.attribute("originalNamespace", createBareDeclRef(ND->getOriginalNamespace())); @@ -827,6 +851,9 @@ void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) { case VarDecl::CInit: JOS.attribute("init", "c"); break; case VarDecl::CallInit: JOS.attribute("init", "call"); break; case VarDecl::ListInit: JOS.attribute("init", "list"); break; + case VarDecl::ParenListInit: + JOS.attribute("init", "paren-list"); + break; } } attributeOnlyIfTrue("isParameterPack", VD->isParameterPack()); @@ -893,6 +920,11 @@ void JSONNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *RD) { } } +void JSONNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { + VisitNamedDecl(D); + JOS.attribute("bufferKind", D->isCBuffer() ? "cbuffer" : "tbuffer"); +} + void JSONNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { VisitNamedDecl(D); JOS.attribute("tagUsed", D->wasDeclaredWithTypename() ? "typename" : "class"); diff --git a/clang/lib/AST/Linkage.h b/clang/lib/AST/Linkage.h index cd50d138790a..31f384eb75d0 100644 --- a/clang/lib/AST/Linkage.h +++ b/clang/lib/AST/Linkage.h @@ -19,8 +19,8 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" +#include <optional> namespace clang { /// Kinds of LV computation. The linkage side of the computation is @@ -91,11 +91,11 @@ class LinkageComputer { return QueryType(ND, Kind.toBits()); } - llvm::Optional<LinkageInfo> lookup(const NamedDecl *ND, - LVComputationKind Kind) const { + std::optional<LinkageInfo> lookup(const NamedDecl *ND, + LVComputationKind Kind) const { auto Iter = CachedLinkageInfo.find(makeCacheKey(ND, Kind)); if (Iter == CachedLinkageInfo.end()) - return None; + return std::nullopt; return Iter->second; } diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 7ea569c63d9e..31cdad4c8fdd 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -223,6 +223,7 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) if (!MD->isStatic()) ++ArgWords; + uint64_t DefaultPtrWidth = TI.getPointerWidth(LangAS::Default); for (const auto &AT : Proto->param_types()) { // If an argument type is incomplete there is no way to get its size to // correctly encode into the mangling scheme. @@ -230,11 +231,10 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { if (AT->isIncompleteType()) break; // Size should be aligned to pointer size. - ArgWords += - llvm::alignTo(ASTContext.getTypeSize(AT), TI.getPointerWidth(0)) / - TI.getPointerWidth(0); + ArgWords += llvm::alignTo(ASTContext.getTypeSize(AT), DefaultPtrWidth) / + DefaultPtrWidth; } - Out << ((TI.getPointerWidth(0) / 8) * ArgWords); + Out << ((DefaultPtrWidth / 8) * ArgWords); } void MangleContext::mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream &Out) { diff --git a/clang/lib/AST/MicrosoftCXXABI.cpp b/clang/lib/AST/MicrosoftCXXABI.cpp index 7f4a7b2b9381..263e263eba7c 100644 --- a/clang/lib/AST/MicrosoftCXXABI.cpp +++ b/clang/lib/AST/MicrosoftCXXABI.cpp @@ -303,7 +303,7 @@ CXXABI::MemberPointerInfo MicrosoftCXXABI::getMemberPointerInfo( // The nominal struct is laid out with pointers followed by ints and aligned // to a pointer width if any are present and an int width otherwise. const TargetInfo &Target = Context.getTargetInfo(); - unsigned PtrSize = Target.getPointerWidth(0); + unsigned PtrSize = Target.getPointerWidth(LangAS::Default); unsigned IntSize = Target.getIntWidth(); unsigned Ptrs, Ints; @@ -318,7 +318,7 @@ CXXABI::MemberPointerInfo MicrosoftCXXABI::getMemberPointerInfo( if (Ptrs + Ints > 1 && Target.getTriple().isArch32Bit()) MPI.Align = 64; else if (Ptrs) - MPI.Align = Target.getPointerAlign(0); + MPI.Align = Target.getPointerAlign(LangAS::Default); else MPI.Align = Target.getIntAlign(); diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 09075e60142a..cdd2c93c4b14 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -35,6 +35,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/xxhash.h" +#include <optional> using namespace clang; @@ -142,8 +143,8 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext { llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator; llvm::DenseMap<const NamedDecl *, unsigned> Uniquifier; llvm::DenseMap<const CXXRecordDecl *, unsigned> LambdaIds; - llvm::DenseMap<const NamedDecl *, unsigned> SEHFilterIds; - llvm::DenseMap<const NamedDecl *, unsigned> SEHFinallyIds; + llvm::DenseMap<GlobalDecl, unsigned> SEHFilterIds; + llvm::DenseMap<GlobalDecl, unsigned> SEHFinallyIds; SmallString<16> AnonymousNamespaceHash; public: @@ -201,9 +202,9 @@ public: void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicAtExitDestructor(const VarDecl *D, raw_ostream &Out) override; - void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, + void mangleSEHFilterExpression(GlobalDecl EnclosingDecl, raw_ostream &Out) override; - void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl, + void mangleSEHFinallyBlock(GlobalDecl EnclosingDecl, raw_ostream &Out) override; void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override; bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { @@ -340,22 +341,22 @@ public: MicrosoftCXXNameMangler(MicrosoftMangleContextImpl &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(nullptr), StructorType(-1), TemplateArgStringStorage(TemplateArgStringStorageAlloc), - PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == - 64) {} + PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth( + LangAS::Default) == 64) {} MicrosoftCXXNameMangler(MicrosoftMangleContextImpl &C, raw_ostream &Out_, const CXXConstructorDecl *D, CXXCtorType Type) : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), TemplateArgStringStorage(TemplateArgStringStorageAlloc), - PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == - 64) {} + PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth( + LangAS::Default) == 64) {} MicrosoftCXXNameMangler(MicrosoftMangleContextImpl &C, raw_ostream &Out_, const CXXDestructorDecl *D, CXXDtorType Type) : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), TemplateArgStringStorage(TemplateArgStringStorageAlloc), - PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == - 64) {} + PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth( + LangAS::Default) == 64) {} raw_ostream &getStream() const { return Out; } @@ -376,7 +377,7 @@ public: void mangleBits(llvm::APInt Number); void mangleTagTypeKind(TagTypeKind TK); void mangleArtificialTagType(TagTypeKind TK, StringRef UnqualifiedName, - ArrayRef<StringRef> NestedNames = None); + ArrayRef<StringRef> NestedNames = std::nullopt); void mangleAddressSpaceType(QualType T, Qualifiers Quals, SourceRange Range); void mangleType(QualType T, SourceRange Range, QualifierMangleMode QMM = QMM_Mangle); @@ -776,7 +777,7 @@ void MicrosoftCXXNameMangler::mangleVirtualMemPtrThunk( const CXXMethodDecl *MD, const MethodVFTableLocation &ML) { // Get the vftable offset. CharUnits PointerWidth = getASTContext().toCharUnitsFromBits( - getASTContext().getTargetInfo().getPointerWidth(0)); + getASTContext().getTargetInfo().getPointerWidth(LangAS::Default)); uint64_t OffsetInVFTable = ML.Index * PointerWidth.getQuantity(); Out << "?_9"; @@ -838,6 +839,9 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) { case APFloat::S_x87DoubleExtended: Out << 'X'; break; case APFloat::S_IEEEquad: Out << 'Y'; break; case APFloat::S_PPCDoubleDouble: Out << 'Z'; break; + case APFloat::S_Float8E5M2: + case APFloat::S_Float8E4M3FN: + llvm_unreachable("Tried to mangle unexpected APFloat semantics"); } mangleBits(Number.bitcastToAPInt()); @@ -1505,7 +1509,7 @@ void MicrosoftCXXNameMangler::mangleIntegerLiteral( void MicrosoftCXXNameMangler::mangleExpression( const Expr *E, const NonTypeTemplateParmDecl *PD) { // See if this is a constant expression. - if (Optional<llvm::APSInt> Value = + if (std::optional<llvm::APSInt> Value = E->getIntegerConstantExpr(Context.getASTContext())) { mangleIntegerLiteral(*Value, PD, E->getType()); return; @@ -2469,6 +2473,10 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, Out << "$halff@"; break; + case BuiltinType::BFloat16: + mangleArtificialTagType(TTK_Struct, "__bf16", {"__clang"}); + break; + #define SVE_TYPE(Name, Id, SingletonId) \ case BuiltinType::Id: #include "clang/Basic/AArch64SVEACLETypes.def" @@ -2501,7 +2509,6 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, case BuiltinType::SatUShortFract: case BuiltinType::SatUFract: case BuiltinType::SatULongFract: - case BuiltinType::BFloat16: case BuiltinType::Ibm128: case BuiltinType::Float128: { DiagnosticsEngine &Diags = Context.getDiags(); @@ -3070,14 +3077,17 @@ bool MicrosoftCXXNameMangler::isArtificialTagType(QualType T) const { void MicrosoftCXXNameMangler::mangleType(const VectorType *T, Qualifiers Quals, SourceRange Range) { - const BuiltinType *ET = T->getElementType()->getAs<BuiltinType>(); - assert(ET && "vectors with non-builtin elements are unsupported"); + QualType EltTy = T->getElementType(); + const BuiltinType *ET = EltTy->getAs<BuiltinType>(); + const BitIntType *BitIntTy = EltTy->getAs<BitIntType>(); + assert((ET || BitIntTy) && + "vectors with non-builtin/_BitInt elements are unsupported"); uint64_t Width = getASTContext().getTypeSize(T); // Pattern match exactly the typedefs in our intrinsic headers. Anything that // doesn't match the Intel types uses a custom mangling below. size_t OutSizeBefore = Out.tell(); if (!isa<ExtVectorType>(T)) { - if (getASTContext().getTargetInfo().getTriple().isX86()) { + if (getASTContext().getTargetInfo().getTriple().isX86() && ET) { if (Width == 64 && ET->getKind() == BuiltinType::LongLong) { mangleArtificialTagType(TTK_Union, "__m64"); } else if (Width >= 128) { @@ -3102,7 +3112,8 @@ void MicrosoftCXXNameMangler::mangleType(const VectorType *T, Qualifiers Quals, MicrosoftCXXNameMangler Extra(Context, Stream); Stream << "?$"; Extra.mangleSourceName("__vector"); - Extra.mangleType(QualType(ET, 0), Range, QMM_Escape); + Extra.mangleType(QualType(ET ? static_cast<const Type *>(ET) : BitIntTy, 0), + Range, QMM_Escape); Extra.mangleIntegerLiteral(llvm::APSInt::getUnsigned(T->getNumElements())); mangleArtificialTagType(TTK_Union, TemplateMangling, {"__clang"}); @@ -3720,7 +3731,7 @@ void MicrosoftMangleContextImpl::mangleCXXRTTICompleteObjectLocator( } void MicrosoftMangleContextImpl::mangleSEHFilterExpression( - const NamedDecl *EnclosingDecl, raw_ostream &Out) { + GlobalDecl EnclosingDecl, raw_ostream &Out) { msvc_hashing_ostream MHO(Out); MicrosoftCXXNameMangler Mangler(*this, MHO); // The function body is in the same comdat as the function with the handler, @@ -3732,7 +3743,7 @@ void MicrosoftMangleContextImpl::mangleSEHFilterExpression( } void MicrosoftMangleContextImpl::mangleSEHFinallyBlock( - const NamedDecl *EnclosingDecl, raw_ostream &Out) { + GlobalDecl EnclosingDecl, raw_ostream &Out) { msvc_hashing_ostream MHO(Out); MicrosoftCXXNameMangler Mangler(*this, MHO); // The function body is in the same comdat as the function with the handler, diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index db7878e18c42..3621a2eaa573 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -11,6 +11,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "llvm/ADT/StringSwitch.h" +#include <optional> using namespace clang; @@ -142,14 +143,15 @@ Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { return NSArraySelectors[MK]; } -Optional<NSAPI::NSArrayMethodKind> NSAPI::getNSArrayMethodKind(Selector Sel) { +std::optional<NSAPI::NSArrayMethodKind> +NSAPI::getNSArrayMethodKind(Selector Sel) { for (unsigned i = 0; i != NumNSArrayMethods; ++i) { NSArrayMethodKind MK = NSArrayMethodKind(i); if (Sel == getNSArraySelector(MK)) return MK; } - return None; + return std::nullopt; } Selector NSAPI::getNSDictionarySelector( @@ -243,7 +245,7 @@ Selector NSAPI::getNSDictionarySelector( return NSDictionarySelectors[MK]; } -Optional<NSAPI::NSDictionaryMethodKind> +std::optional<NSAPI::NSDictionaryMethodKind> NSAPI::getNSDictionaryMethodKind(Selector Sel) { for (unsigned i = 0; i != NumNSDictionaryMethods; ++i) { NSDictionaryMethodKind MK = NSDictionaryMethodKind(i); @@ -251,7 +253,7 @@ NSAPI::getNSDictionaryMethodKind(Selector Sel) { return MK; } - return None; + return std::nullopt; } Selector NSAPI::getNSSetSelector(NSSetMethodKind MK) const { @@ -300,15 +302,14 @@ Selector NSAPI::getNSSetSelector(NSSetMethodKind MK) const { return NSSetSelectors[MK]; } -Optional<NSAPI::NSSetMethodKind> -NSAPI::getNSSetMethodKind(Selector Sel) { +std::optional<NSAPI::NSSetMethodKind> NSAPI::getNSSetMethodKind(Selector Sel) { for (unsigned i = 0; i != NumNSSetMethods; ++i) { NSSetMethodKind MK = NSSetMethodKind(i); if (Sel == getNSSetSelector(MK)) return MK; } - return None; + return std::nullopt; } Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, @@ -363,7 +364,7 @@ Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, return Sels[MK]; } -Optional<NSAPI::NSNumberLiteralMethodKind> +std::optional<NSAPI::NSNumberLiteralMethodKind> NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { for (unsigned i = 0; i != NumNSNumberLiteralMethods; ++i) { NSNumberLiteralMethodKind MK = NSNumberLiteralMethodKind(i); @@ -371,14 +372,14 @@ NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { return MK; } - return None; + return std::nullopt; } -Optional<NSAPI::NSNumberLiteralMethodKind> +std::optional<NSAPI::NSNumberLiteralMethodKind> NSAPI::getNSNumberFactoryMethodKind(QualType T) const { const BuiltinType *BT = T->getAs<BuiltinType>(); if (!BT) - return None; + return std::nullopt; const TypedefType *TDT = T->getAs<TypedefType>(); if (TDT) { @@ -496,7 +497,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { break; } - return None; + return std::nullopt; } /// Returns true if \param T is a typedef of "BOOL" in objective-c. diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index 8f19d80cbdc5..36f2c47b3000 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -280,14 +280,14 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, case TypeSpecWithTemplate: OS << "template "; // Fall through to print the type. - LLVM_FALLTHROUGH; + [[fallthrough]]; case TypeSpec: { const auto *Record = dyn_cast_or_null<ClassTemplateSpecializationDecl>(getAsRecordDecl()); if (ResolveTemplateArguments && Record) { // Print the type trait with resolved template parameters. - Record->printName(OS); + Record->printName(OS, Policy); printTemplateArgumentList( OS, Record->getTemplateArgs().asArray(), Policy, Record->getSpecializedTemplate()->getTemplateParameters()); diff --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp new file mode 100644 index 000000000000..b3fe070889c5 --- /dev/null +++ b/clang/lib/AST/ODRDiagsEmitter.cpp @@ -0,0 +1,2206 @@ +//===-- ODRDiagsEmitter.cpp - Diagnostics for ODR mismatches ----*- C++ -*-===// +// +// 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 "clang/AST/ODRDiagsEmitter.h" +#include "clang/AST/DeclFriend.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/ODRHash.h" +#include "clang/Basic/DiagnosticAST.h" +#include "clang/Basic/Module.h" + +using namespace clang; + +static unsigned computeODRHash(QualType Ty) { + ODRHash Hasher; + Hasher.AddQualType(Ty); + return Hasher.CalculateHash(); +} + +static unsigned computeODRHash(const Stmt *S) { + ODRHash Hasher; + Hasher.AddStmt(S); + return Hasher.CalculateHash(); +} + +static unsigned computeODRHash(const Decl *D) { + assert(D); + ODRHash Hasher; + Hasher.AddSubDecl(D); + return Hasher.CalculateHash(); +} + +static unsigned computeODRHash(const TemplateArgument &TA) { + ODRHash Hasher; + Hasher.AddTemplateArgument(TA); + return Hasher.CalculateHash(); +} + +std::string ODRDiagsEmitter::getOwningModuleNameForDiagnostic(const Decl *D) { + // If we know the owning module, use it. + if (Module *M = D->getImportedOwningModule()) + return M->getFullModuleName(); + + // Not from a module. + return {}; +} + +template <typename MethodT> +static bool diagnoseSubMismatchMethodParameters(DiagnosticsEngine &Diags, + const NamedDecl *FirstContainer, + StringRef FirstModule, + StringRef SecondModule, + const MethodT *FirstMethod, + const MethodT *SecondMethod) { + enum DiagMethodType { + DiagMethod, + DiagConstructor, + DiagDestructor, + }; + auto GetDiagMethodType = [](const NamedDecl *D) { + if (isa<CXXConstructorDecl>(D)) + return DiagConstructor; + if (isa<CXXDestructorDecl>(D)) + return DiagDestructor; + return DiagMethod; + }; + + enum ODRMethodParametersDifference { + NumberParameters, + ParameterType, + ParameterName, + }; + auto DiagError = [&Diags, &GetDiagMethodType, FirstContainer, FirstModule, + FirstMethod](ODRMethodParametersDifference DiffType) { + DeclarationName FirstName = FirstMethod->getDeclName(); + DiagMethodType FirstMethodType = GetDiagMethodType(FirstMethod); + return Diags.Report(FirstMethod->getLocation(), + diag::err_module_odr_violation_method_params) + << FirstContainer << FirstModule.empty() << FirstModule + << FirstMethod->getSourceRange() << DiffType << FirstMethodType + << FirstName; + }; + auto DiagNote = [&Diags, &GetDiagMethodType, SecondModule, + SecondMethod](ODRMethodParametersDifference DiffType) { + DeclarationName SecondName = SecondMethod->getDeclName(); + DiagMethodType SecondMethodType = GetDiagMethodType(SecondMethod); + return Diags.Report(SecondMethod->getLocation(), + diag::note_module_odr_violation_method_params) + << SecondModule.empty() << SecondModule + << SecondMethod->getSourceRange() << DiffType << SecondMethodType + << SecondName; + }; + + const unsigned FirstNumParameters = FirstMethod->param_size(); + const unsigned SecondNumParameters = SecondMethod->param_size(); + if (FirstNumParameters != SecondNumParameters) { + DiagError(NumberParameters) << FirstNumParameters; + DiagNote(NumberParameters) << SecondNumParameters; + return true; + } + + for (unsigned I = 0; I < FirstNumParameters; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); + + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs<DecayedType>()) { + DiagError(ParameterType) << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagError(ParameterType) << (I + 1) << FirstParamType << false; + } + + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs<DecayedType>()) { + DiagNote(ParameterType) << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagNote(ParameterType) << (I + 1) << SecondParamType << false; + } + return true; + } + + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + DiagError(ParameterName) << (I + 1) << FirstParamName; + DiagNote(ParameterName) << (I + 1) << SecondParamName; + return true; + } + } + + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchField( + const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + const FieldDecl *FirstField, const FieldDecl *SecondField) const { + enum ODRFieldDifference { + FieldName, + FieldTypeName, + FieldSingleBitField, + FieldDifferentWidthBitField, + FieldSingleMutable, + FieldSingleInitializer, + FieldDifferentInitializers, + }; + + auto DiagError = [FirstRecord, FirstField, FirstModule, + this](ODRFieldDifference DiffType) { + return Diag(FirstField->getLocation(), diag::err_module_odr_violation_field) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondField, SecondModule, + this](ODRFieldDifference DiffType) { + return Diag(SecondField->getLocation(), + diag::note_module_odr_violation_field) + << SecondModule.empty() << SecondModule << SecondField->getSourceRange() << DiffType; + }; + + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + DiagError(FieldName) << FirstII; + DiagNote(FieldName) << SecondII; + return true; + } + + QualType FirstType = FirstField->getType(); + QualType SecondType = SecondField->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(FieldTypeName) << FirstII << FirstType; + DiagNote(FieldTypeName) << SecondII << SecondType; + return true; + } + + assert(Context.hasSameType(FirstField->getType(), SecondField->getType())); + (void)Context; + + const bool IsFirstBitField = FirstField->isBitField(); + const bool IsSecondBitField = SecondField->isBitField(); + if (IsFirstBitField != IsSecondBitField) { + DiagError(FieldSingleBitField) << FirstII << IsFirstBitField; + DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField; + return true; + } + + if (IsFirstBitField && IsSecondBitField) { + unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth()); + unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth()); + if (FirstBitWidthHash != SecondBitWidthHash) { + DiagError(FieldDifferentWidthBitField) + << FirstII << FirstField->getBitWidth()->getSourceRange(); + DiagNote(FieldDifferentWidthBitField) + << SecondII << SecondField->getBitWidth()->getSourceRange(); + return true; + } + } + + if (!LangOpts.CPlusPlus) + return false; + + const bool IsFirstMutable = FirstField->isMutable(); + const bool IsSecondMutable = SecondField->isMutable(); + if (IsFirstMutable != IsSecondMutable) { + DiagError(FieldSingleMutable) << FirstII << IsFirstMutable; + DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable; + return true; + } + + const Expr *FirstInitializer = FirstField->getInClassInitializer(); + const Expr *SecondInitializer = SecondField->getInClassInitializer(); + if ((!FirstInitializer && SecondInitializer) || + (FirstInitializer && !SecondInitializer)) { + DiagError(FieldSingleInitializer) + << FirstII << (FirstInitializer != nullptr); + DiagNote(FieldSingleInitializer) + << SecondII << (SecondInitializer != nullptr); + return true; + } + + if (FirstInitializer && SecondInitializer) { + unsigned FirstInitHash = computeODRHash(FirstInitializer); + unsigned SecondInitHash = computeODRHash(SecondInitializer); + if (FirstInitHash != SecondInitHash) { + DiagError(FieldDifferentInitializers) + << FirstII << FirstInitializer->getSourceRange(); + DiagNote(FieldDifferentInitializers) + << SecondII << SecondInitializer->getSourceRange(); + return true; + } + } + + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchTypedef( + const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD, + bool IsTypeAlias) const { + enum ODRTypedefDifference { + TypedefName, + TypedefType, + }; + + auto DiagError = [FirstRecord, FirstTD, FirstModule, + this](ODRTypedefDifference DiffType) { + return Diag(FirstTD->getLocation(), diag::err_module_odr_violation_typedef) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstTD->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondTD, SecondModule, + this](ODRTypedefDifference DiffType) { + return Diag(SecondTD->getLocation(), + diag::note_module_odr_violation_typedef) + << SecondModule << SecondTD->getSourceRange() << DiffType; + }; + + DeclarationName FirstName = FirstTD->getDeclName(); + DeclarationName SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + DiagError(TypedefName) << IsTypeAlias << FirstName; + DiagNote(TypedefName) << IsTypeAlias << SecondName; + return true; + } + + QualType FirstType = FirstTD->getUnderlyingType(); + QualType SecondType = SecondTD->getUnderlyingType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType; + DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType; + return true; + } + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord, + StringRef FirstModule, + StringRef SecondModule, + const VarDecl *FirstVD, + const VarDecl *SecondVD) const { + enum ODRVarDifference { + VarName, + VarType, + VarSingleInitializer, + VarDifferentInitializer, + VarConstexpr, + }; + + auto DiagError = [FirstRecord, FirstVD, FirstModule, + this](ODRVarDifference DiffType) { + return Diag(FirstVD->getLocation(), diag::err_module_odr_violation_variable) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstVD->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) { + return Diag(SecondVD->getLocation(), + diag::note_module_odr_violation_variable) + << SecondModule << SecondVD->getSourceRange() << DiffType; + }; + + DeclarationName FirstName = FirstVD->getDeclName(); + DeclarationName SecondName = SecondVD->getDeclName(); + if (FirstName != SecondName) { + DiagError(VarName) << FirstName; + DiagNote(VarName) << SecondName; + return true; + } + + QualType FirstType = FirstVD->getType(); + QualType SecondType = SecondVD->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(VarType) << FirstName << FirstType; + DiagNote(VarType) << SecondName << SecondType; + return true; + } + + if (!LangOpts.CPlusPlus) + return false; + + const Expr *FirstInit = FirstVD->getInit(); + const Expr *SecondInit = SecondVD->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagError(VarSingleInitializer) + << FirstName << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagNote(VarSingleInitializer) + << SecondName << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(VarDifferentInitializer) + << FirstName << FirstInit->getSourceRange(); + DiagNote(VarDifferentInitializer) + << SecondName << SecondInit->getSourceRange(); + return true; + } + + const bool FirstIsConstexpr = FirstVD->isConstexpr(); + const bool SecondIsConstexpr = SecondVD->isConstexpr(); + if (FirstIsConstexpr != SecondIsConstexpr) { + DiagError(VarConstexpr) << FirstName << FirstIsConstexpr; + DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr; + return true; + } + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchProtocols( + const ObjCProtocolList &FirstProtocols, + const ObjCContainerDecl *FirstContainer, StringRef FirstModule, + const ObjCProtocolList &SecondProtocols, + const ObjCContainerDecl *SecondContainer, StringRef SecondModule) const { + // Keep in sync with err_module_odr_violation_referenced_protocols. + enum ODRReferencedProtocolDifference { + NumProtocols, + ProtocolType, + }; + auto DiagRefProtocolError = [FirstContainer, FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRReferencedProtocolDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_referenced_protocols) + << FirstContainer << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagRefProtocolNote = [SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRReferencedProtocolDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_referenced_protocols) + << SecondModule.empty() << SecondModule << Range << DiffType; + }; + auto GetProtoListSourceRange = [](const ObjCProtocolList &PL) { + if (PL.empty()) + return SourceRange(); + return SourceRange(*PL.loc_begin(), *std::prev(PL.loc_end())); + }; + + if (FirstProtocols.size() != SecondProtocols.size()) { + DiagRefProtocolError(FirstContainer->getLocation(), + GetProtoListSourceRange(FirstProtocols), NumProtocols) + << FirstProtocols.size(); + DiagRefProtocolNote(SecondContainer->getLocation(), + GetProtoListSourceRange(SecondProtocols), NumProtocols) + << SecondProtocols.size(); + return true; + } + + for (unsigned I = 0, E = FirstProtocols.size(); I != E; ++I) { + const ObjCProtocolDecl *FirstProtocol = FirstProtocols[I]; + const ObjCProtocolDecl *SecondProtocol = SecondProtocols[I]; + DeclarationName FirstProtocolName = FirstProtocol->getDeclName(); + DeclarationName SecondProtocolName = SecondProtocol->getDeclName(); + if (FirstProtocolName != SecondProtocolName) { + SourceLocation FirstLoc = *(FirstProtocols.loc_begin() + I); + SourceLocation SecondLoc = *(SecondProtocols.loc_begin() + I); + SourceRange EmptyRange; + DiagRefProtocolError(FirstLoc, EmptyRange, ProtocolType) + << (I + 1) << FirstProtocolName; + DiagRefProtocolNote(SecondLoc, EmptyRange, ProtocolType) + << (I + 1) << SecondProtocolName; + return true; + } + } + + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchObjCMethod( + const NamedDecl *FirstObjCContainer, StringRef FirstModule, + StringRef SecondModule, const ObjCMethodDecl *FirstMethod, + const ObjCMethodDecl *SecondMethod) const { + enum ODRMethodDifference { + ReturnType, + InstanceOrClass, + ControlLevel, // optional/required + DesignatedInitializer, + Directness, + Name, + }; + + auto DiagError = [FirstObjCContainer, FirstModule, FirstMethod, + this](ODRMethodDifference DiffType) { + return Diag(FirstMethod->getLocation(), + diag::err_module_odr_violation_objc_method) + << FirstObjCContainer << FirstModule.empty() << FirstModule + << FirstMethod->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondModule, SecondMethod, + this](ODRMethodDifference DiffType) { + return Diag(SecondMethod->getLocation(), + diag::note_module_odr_violation_objc_method) + << SecondModule.empty() << SecondModule + << SecondMethod->getSourceRange() << DiffType; + }; + + if (computeODRHash(FirstMethod->getReturnType()) != + computeODRHash(SecondMethod->getReturnType())) { + DiagError(ReturnType) << FirstMethod << FirstMethod->getReturnType(); + DiagNote(ReturnType) << SecondMethod << SecondMethod->getReturnType(); + return true; + } + + if (FirstMethod->isInstanceMethod() != SecondMethod->isInstanceMethod()) { + DiagError(InstanceOrClass) + << FirstMethod << FirstMethod->isInstanceMethod(); + DiagNote(InstanceOrClass) + << SecondMethod << SecondMethod->isInstanceMethod(); + return true; + } + if (FirstMethod->getImplementationControl() != + SecondMethod->getImplementationControl()) { + DiagError(ControlLevel) << FirstMethod->getImplementationControl(); + DiagNote(ControlLevel) << SecondMethod->getImplementationControl(); + return true; + } + if (FirstMethod->isThisDeclarationADesignatedInitializer() != + SecondMethod->isThisDeclarationADesignatedInitializer()) { + DiagError(DesignatedInitializer) + << FirstMethod + << FirstMethod->isThisDeclarationADesignatedInitializer(); + DiagNote(DesignatedInitializer) + << SecondMethod + << SecondMethod->isThisDeclarationADesignatedInitializer(); + return true; + } + if (FirstMethod->isDirectMethod() != SecondMethod->isDirectMethod()) { + DiagError(Directness) << FirstMethod << FirstMethod->isDirectMethod(); + DiagNote(Directness) << SecondMethod << SecondMethod->isDirectMethod(); + return true; + } + if (diagnoseSubMismatchMethodParameters(Diags, FirstObjCContainer, + FirstModule, SecondModule, + FirstMethod, SecondMethod)) + return true; + + // Check method name *after* looking at the parameters otherwise we get a + // less ideal diagnostics: a ObjCMethodName mismatch given that selectors + // for different parameters are likely to be different. + DeclarationName FirstName = FirstMethod->getDeclName(); + DeclarationName SecondName = SecondMethod->getDeclName(); + if (FirstName != SecondName) { + DiagError(Name) << FirstName; + DiagNote(Name) << SecondName; + return true; + } + + return false; +} + +bool ODRDiagsEmitter::diagnoseSubMismatchObjCProperty( + const NamedDecl *FirstObjCContainer, StringRef FirstModule, + StringRef SecondModule, const ObjCPropertyDecl *FirstProp, + const ObjCPropertyDecl *SecondProp) const { + enum ODRPropertyDifference { + Name, + Type, + ControlLevel, // optional/required + Attribute, + }; + + auto DiagError = [FirstObjCContainer, FirstModule, FirstProp, + this](SourceLocation Loc, ODRPropertyDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_objc_property) + << FirstObjCContainer << FirstModule.empty() << FirstModule + << FirstProp->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondModule, SecondProp, + this](SourceLocation Loc, ODRPropertyDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_objc_property) + << SecondModule.empty() << SecondModule + << SecondProp->getSourceRange() << DiffType; + }; + + IdentifierInfo *FirstII = FirstProp->getIdentifier(); + IdentifierInfo *SecondII = SecondProp->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + DiagError(FirstProp->getLocation(), Name) << FirstII; + DiagNote(SecondProp->getLocation(), Name) << SecondII; + return true; + } + if (computeODRHash(FirstProp->getType()) != + computeODRHash(SecondProp->getType())) { + DiagError(FirstProp->getLocation(), Type) + << FirstII << FirstProp->getType(); + DiagNote(SecondProp->getLocation(), Type) + << SecondII << SecondProp->getType(); + return true; + } + if (FirstProp->getPropertyImplementation() != + SecondProp->getPropertyImplementation()) { + DiagError(FirstProp->getLocation(), ControlLevel) + << FirstProp->getPropertyImplementation(); + DiagNote(SecondProp->getLocation(), ControlLevel) + << SecondProp->getPropertyImplementation(); + return true; + } + + // Go over the property attributes and stop at the first mismatch. + unsigned FirstAttrs = (unsigned)FirstProp->getPropertyAttributes(); + unsigned SecondAttrs = (unsigned)SecondProp->getPropertyAttributes(); + if (FirstAttrs != SecondAttrs) { + for (unsigned I = 0; I < NumObjCPropertyAttrsBits; ++I) { + unsigned CheckedAttr = (1 << I); + if ((FirstAttrs & CheckedAttr) == (SecondAttrs & CheckedAttr)) + continue; + + bool IsFirstWritten = + (unsigned)FirstProp->getPropertyAttributesAsWritten() & CheckedAttr; + bool IsSecondWritten = + (unsigned)SecondProp->getPropertyAttributesAsWritten() & CheckedAttr; + DiagError(IsFirstWritten ? FirstProp->getLParenLoc() + : FirstProp->getLocation(), + Attribute) + << FirstII << (I + 1) << IsFirstWritten; + DiagNote(IsSecondWritten ? SecondProp->getLParenLoc() + : SecondProp->getLocation(), + Attribute) + << SecondII << (I + 1); + return true; + } + } + + return false; +} + +ODRDiagsEmitter::DiffResult +ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes, + DeclHashes &SecondHashes) { + auto DifferenceSelector = [](const Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + break; + } + llvm_unreachable("Invalid access specifier"); + case Decl::StaticAssert: + return StaticAssert; + case Decl::Field: + return Field; + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + return CXXMethod; + case Decl::TypeAlias: + return TypeAlias; + case Decl::Typedef: + return TypeDef; + case Decl::Var: + return Var; + case Decl::Friend: + return Friend; + case Decl::FunctionTemplate: + return FunctionTemplate; + case Decl::ObjCMethod: + return ObjCMethod; + case Decl::ObjCIvar: + return ObjCIvar; + case Decl::ObjCProperty: + return ObjCProperty; + } + }; + + DiffResult DR; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && + FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; + continue; + } + + DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + DR.SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + DR.FirstDiffType = + DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; + DR.SecondDiffType = + DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; + return DR; + } + return DR; +} + +void ODRDiagsEmitter::diagnoseSubMismatchUnexpected( + DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, + const NamedDecl *SecondRecord, StringRef SecondModule) const { + Diag(FirstRecord->getLocation(), + diag::err_module_odr_violation_different_definitions) + << FirstRecord << FirstModule.empty() << FirstModule; + + if (DR.FirstDecl) { + Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) + << FirstRecord << DR.FirstDecl->getSourceRange(); + } + + Diag(SecondRecord->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + + if (DR.SecondDecl) { + Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) + << DR.SecondDecl->getSourceRange(); + } +} + +void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds( + DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, + const NamedDecl *SecondRecord, StringRef SecondModule) const { + auto GetMismatchedDeclLoc = [](const NamedDecl *Container, + ODRMismatchDecl DiffType, const Decl *D) { + SourceLocation Loc; + SourceRange Range; + if (DiffType == EndOfClass) { + if (auto *Tag = dyn_cast<TagDecl>(Container)) + Loc = Tag->getBraceRange().getEnd(); + else if (auto *IF = dyn_cast<ObjCInterfaceDecl>(Container)) + Loc = IF->getAtEndRange().getBegin(); + else + Loc = Container->getEndLoc(); + } else { + Loc = D->getLocation(); + Range = D->getSourceRange(); + } + return std::make_pair(Loc, Range); + }; + + auto FirstDiagInfo = + GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); + Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDiagInfo.second << DR.FirstDiffType; + + auto SecondDiagInfo = + GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); + Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) + << SecondModule.empty() << SecondModule << SecondDiagInfo.second + << DR.SecondDiffType; +} + +bool ODRDiagsEmitter::diagnoseMismatch( + const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord, + const struct CXXRecordDecl::DefinitionData *SecondDD) const { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstRecord == SecondRecord) + return false; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + + const struct CXXRecordDecl::DefinitionData *FirstDD = + FirstRecord->DefinitionData; + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + + // Diagnostics from DefinitionData are emitted here. + if (FirstDD != SecondDD) { + // Keep in sync with err_module_odr_violation_definition_data. + enum ODRDefinitionDataDifference { + NumBases, + NumVBases, + BaseType, + BaseVirtual, + BaseAccess, + }; + auto DiagBaseError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_definition_data) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagBaseNote = [&SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_definition_data) + << SecondModule << Range << DiffType; + }; + auto GetSourceRange = [](const struct CXXRecordDecl::DefinitionData *DD) { + unsigned NumBases = DD->NumBases; + if (NumBases == 0) + return SourceRange(); + ArrayRef<CXXBaseSpecifier> bases = DD->bases(); + return SourceRange(bases[0].getBeginLoc(), + bases[NumBases - 1].getEndLoc()); + }; + + unsigned FirstNumBases = FirstDD->NumBases; + unsigned FirstNumVBases = FirstDD->NumVBases; + unsigned SecondNumBases = SecondDD->NumBases; + unsigned SecondNumVBases = SecondDD->NumVBases; + if (FirstNumBases != SecondNumBases) { + DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumBases) + << FirstNumBases; + DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumBases) + << SecondNumBases; + return true; + } + + if (FirstNumVBases != SecondNumVBases) { + DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumVBases) + << FirstNumVBases; + DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumVBases) + << SecondNumVBases; + return true; + } + + ArrayRef<CXXBaseSpecifier> FirstBases = FirstDD->bases(); + ArrayRef<CXXBaseSpecifier> SecondBases = SecondDD->bases(); + for (unsigned I = 0; I < FirstNumBases; ++I) { + const CXXBaseSpecifier FirstBase = FirstBases[I]; + const CXXBaseSpecifier SecondBase = SecondBases[I]; + if (computeODRHash(FirstBase.getType()) != + computeODRHash(SecondBase.getType())) { + DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), + BaseType) + << (I + 1) << FirstBase.getType(); + DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), + BaseType) + << (I + 1) << SecondBase.getType(); + return true; + } + + if (FirstBase.isVirtual() != SecondBase.isVirtual()) { + DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), + BaseVirtual) + << (I + 1) << FirstBase.isVirtual() << FirstBase.getType(); + DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), + BaseVirtual) + << (I + 1) << SecondBase.isVirtual() << SecondBase.getType(); + return true; + } + + if (FirstBase.getAccessSpecifierAsWritten() != + SecondBase.getAccessSpecifierAsWritten()) { + DiagBaseError(FirstRecord->getLocation(), FirstBase.getSourceRange(), + BaseAccess) + << (I + 1) << FirstBase.getType() + << (int)FirstBase.getAccessSpecifierAsWritten(); + DiagBaseNote(SecondRecord->getLocation(), SecondBase.getSourceRange(), + BaseAccess) + << (I + 1) << SecondBase.getType() + << (int)SecondBase.getAccessSpecifierAsWritten(); + return true; + } + } + } + + const ClassTemplateDecl *FirstTemplate = + FirstRecord->getDescribedClassTemplate(); + const ClassTemplateDecl *SecondTemplate = + SecondRecord->getDescribedClassTemplate(); + + assert(!FirstTemplate == !SecondTemplate && + "Both pointers should be null or non-null"); + + if (FirstTemplate && SecondTemplate) { + ArrayRef<const NamedDecl *> FirstTemplateParams = + FirstTemplate->getTemplateParameters()->asArray(); + ArrayRef<const NamedDecl *> SecondTemplateParams = + SecondTemplate->getTemplateParameters()->asArray(); + assert(FirstTemplateParams.size() == SecondTemplateParams.size() && + "Number of template parameters should be equal."); + for (auto Pair : llvm::zip(FirstTemplateParams, SecondTemplateParams)) { + const NamedDecl *FirstDecl = std::get<0>(Pair); + const NamedDecl *SecondDecl = std::get<1>(Pair); + if (computeODRHash(FirstDecl) == computeODRHash(SecondDecl)) + continue; + + assert(FirstDecl->getKind() == SecondDecl->getKind() && + "Parameter Decl's should be the same kind."); + + enum ODRTemplateDifference { + ParamEmptyName, + ParamName, + ParamSingleDefaultArgument, + ParamDifferentDefaultArgument, + }; + + auto hasDefaultArg = [](const NamedDecl *D) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) + return TTP->hasDefaultArgument() && + !TTP->defaultArgumentWasInherited(); + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) + return NTTP->hasDefaultArgument() && + !NTTP->defaultArgumentWasInherited(); + auto *TTP = cast<TemplateTemplateParmDecl>(D); + return TTP->hasDefaultArgument() && !TTP->defaultArgumentWasInherited(); + }; + bool hasFirstArg = hasDefaultArg(FirstDecl); + bool hasSecondArg = hasDefaultArg(SecondDecl); + + ODRTemplateDifference ErrDiffType; + ODRTemplateDifference NoteDiffType; + + DeclarationName FirstName = FirstDecl->getDeclName(); + DeclarationName SecondName = SecondDecl->getDeclName(); + + if (FirstName != SecondName) { + bool FirstNameEmpty = + FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo(); + bool SecondNameEmpty = + SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo(); + ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName; + NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName; + } else if (hasFirstArg == hasSecondArg) + ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument; + else + ErrDiffType = NoteDiffType = ParamSingleDefaultArgument; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_template_parameter) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg + << FirstName; + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_template_parameter) + << SecondModule << SecondDecl->getSourceRange() << NoteDiffType + << hasSecondArg << SecondName; + return true; + } + } + + auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record, + const DeclContext *DC) { + for (const Decl *D : Record->decls()) { + if (!ODRHash::isSubDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + return true; + } + + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, + SecondRecord, SecondModule); + return true; + } + + // Used with err_module_odr_violation_record and + // note_module_odr_violation_record + enum ODRCXXRecordDifference { + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + MethodName, + MethodDeleted, + MethodDefaulted, + MethodVirtual, + MethodStatic, + MethodVolatile, + MethodConst, + MethodInline, + MethodParameterSingleDefaultArgument, + MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, + MethodSingleBody, + MethodDifferentBody, + FriendTypeFunction, + FriendType, + FriendFunction, + FunctionTemplateDifferentNumberParameters, + FunctionTemplateParameterDifferentKind, + FunctionTemplateParameterName, + FunctionTemplateParameterSingleDefaultArgument, + FunctionTemplateParameterDifferentDefaultArgument, + FunctionTemplateParameterDifferentType, + FunctionTemplatePackParameter, + }; + auto DiagError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRCXXRecordDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_record) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range, + ODRCXXRecordDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_record) + << SecondModule << Range << DiffType; + }; + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case Other: + case EndOfClass: + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case ObjCMethod: + case ObjCIvar: + case ObjCProperty: + llvm_unreachable("Invalid diff type"); + + case StaticAssert: { + const StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl); + const StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl); + + const Expr *FirstExpr = FirstSA->getAssertExpr(); + const Expr *SecondExpr = SecondSA->getAssertExpr(); + unsigned FirstODRHash = computeODRHash(FirstExpr); + unsigned SecondODRHash = computeODRHash(SecondExpr); + if (FirstODRHash != SecondODRHash) { + DiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(), + StaticAssertCondition); + DiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(), + StaticAssertCondition); + return true; + } + + const StringLiteral *FirstStr = FirstSA->getMessage(); + const StringLiteral *SecondStr = SecondSA->getMessage(); + assert((FirstStr || SecondStr) && "Both messages cannot be empty"); + if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { + SourceLocation FirstLoc, SecondLoc; + SourceRange FirstRange, SecondRange; + if (FirstStr) { + FirstLoc = FirstStr->getBeginLoc(); + FirstRange = FirstStr->getSourceRange(); + } else { + FirstLoc = FirstSA->getBeginLoc(); + FirstRange = FirstSA->getSourceRange(); + } + if (SecondStr) { + SecondLoc = SecondStr->getBeginLoc(); + SecondRange = SecondStr->getSourceRange(); + } else { + SecondLoc = SecondSA->getBeginLoc(); + SecondRange = SecondSA->getSourceRange(); + } + DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) + << (FirstStr == nullptr); + DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) + << (SecondStr == nullptr); + return true; + } + + if (FirstStr && SecondStr && + FirstStr->getString() != SecondStr->getString()) { + DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), + StaticAssertMessage); + DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), + StaticAssertMessage); + return true; + } + break; + } + + case Field: { + if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, + cast<FieldDecl>(FirstDecl), + cast<FieldDecl>(SecondDecl))) + return true; + break; + } + + case CXXMethod: { + enum { + DiagMethod, + DiagConstructor, + DiagDestructor, + } FirstMethodType, + SecondMethodType; + auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl *D) { + if (isa<CXXConstructorDecl>(D)) + return DiagConstructor; + if (isa<CXXDestructorDecl>(D)) + return DiagDestructor; + return DiagMethod; + }; + const CXXMethodDecl *FirstMethod = cast<CXXMethodDecl>(FirstDecl); + const CXXMethodDecl *SecondMethod = cast<CXXMethodDecl>(SecondDecl); + FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); + SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); + DeclarationName FirstName = FirstMethod->getDeclName(); + DeclarationName SecondName = SecondMethod->getDeclName(); + auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType, + FirstName](ODRCXXRecordDifference DiffType) { + return DiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), DiffType) + << FirstMethodType << FirstName; + }; + auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType, + SecondName](ODRCXXRecordDifference DiffType) { + return DiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), DiffType) + << SecondMethodType << SecondName; + }; + + if (FirstMethodType != SecondMethodType || FirstName != SecondName) { + DiagMethodError(MethodName); + DiagMethodNote(MethodName); + return true; + } + + const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); + const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); + if (FirstDeleted != SecondDeleted) { + DiagMethodError(MethodDeleted) << FirstDeleted; + DiagMethodNote(MethodDeleted) << SecondDeleted; + return true; + } + + const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); + const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); + if (FirstDefaulted != SecondDefaulted) { + DiagMethodError(MethodDefaulted) << FirstDefaulted; + DiagMethodNote(MethodDefaulted) << SecondDefaulted; + return true; + } + + const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); + const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); + const bool FirstPure = FirstMethod->isPure(); + const bool SecondPure = SecondMethod->isPure(); + if ((FirstVirtual || SecondVirtual) && + (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { + DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; + DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; + return true; + } + + // CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, + // FirstDecl is the canonical Decl of SecondDecl, so the storage + // class needs to be checked instead. + StorageClass FirstStorage = FirstMethod->getStorageClass(); + StorageClass SecondStorage = SecondMethod->getStorageClass(); + const bool FirstStatic = FirstStorage == SC_Static; + const bool SecondStatic = SecondStorage == SC_Static; + if (FirstStatic != SecondStatic) { + DiagMethodError(MethodStatic) << FirstStatic; + DiagMethodNote(MethodStatic) << SecondStatic; + return true; + } + + const bool FirstVolatile = FirstMethod->isVolatile(); + const bool SecondVolatile = SecondMethod->isVolatile(); + if (FirstVolatile != SecondVolatile) { + DiagMethodError(MethodVolatile) << FirstVolatile; + DiagMethodNote(MethodVolatile) << SecondVolatile; + return true; + } + + const bool FirstConst = FirstMethod->isConst(); + const bool SecondConst = SecondMethod->isConst(); + if (FirstConst != SecondConst) { + DiagMethodError(MethodConst) << FirstConst; + DiagMethodNote(MethodConst) << SecondConst; + return true; + } + + const bool FirstInline = FirstMethod->isInlineSpecified(); + const bool SecondInline = SecondMethod->isInlineSpecified(); + if (FirstInline != SecondInline) { + DiagMethodError(MethodInline) << FirstInline; + DiagMethodNote(MethodInline) << SecondInline; + return true; + } + + if (diagnoseSubMismatchMethodParameters(Diags, FirstRecord, + FirstModule, SecondModule, + FirstMethod, SecondMethod)) + return true; + + for (unsigned I = 0, N = FirstMethod->param_size(); I < N; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); + + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagMethodError(MethodParameterSingleDefaultArgument) + << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagMethodNote(MethodParameterSingleDefaultArgument) + << (I + 1) << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagMethodError(MethodParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + DiagMethodNote(MethodParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + return true; + } + } + + const TemplateArgumentList *FirstTemplateArgs = + FirstMethod->getTemplateSpecializationArgs(); + const TemplateArgumentList *SecondTemplateArgs = + SecondMethod->getTemplateSpecializationArgs(); + + if ((FirstTemplateArgs && !SecondTemplateArgs) || + (!FirstTemplateArgs && SecondTemplateArgs)) { + DiagMethodError(MethodNoTemplateArguments) + << (FirstTemplateArgs != nullptr); + DiagMethodNote(MethodNoTemplateArguments) + << (SecondTemplateArgs != nullptr); + return true; + } + + if (FirstTemplateArgs && SecondTemplateArgs) { + // Remove pack expansions from argument list. + auto ExpandTemplateArgumentList = [](const TemplateArgumentList *TAL) { + llvm::SmallVector<const TemplateArgument *, 8> ExpandedList; + for (const TemplateArgument &TA : TAL->asArray()) { + if (TA.getKind() != TemplateArgument::Pack) { + ExpandedList.push_back(&TA); + continue; + } + llvm::append_range(ExpandedList, + llvm::make_pointer_range(TA.getPackAsArray())); + } + return ExpandedList; + }; + llvm::SmallVector<const TemplateArgument *, 8> FirstExpandedList = + ExpandTemplateArgumentList(FirstTemplateArgs); + llvm::SmallVector<const TemplateArgument *, 8> SecondExpandedList = + ExpandTemplateArgumentList(SecondTemplateArgs); + + if (FirstExpandedList.size() != SecondExpandedList.size()) { + DiagMethodError(MethodDifferentNumberTemplateArguments) + << (unsigned)FirstExpandedList.size(); + DiagMethodNote(MethodDifferentNumberTemplateArguments) + << (unsigned)SecondExpandedList.size(); + return true; + } + + for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { + const TemplateArgument &FirstTA = *FirstExpandedList[i], + &SecondTA = *SecondExpandedList[i]; + if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) + continue; + + DiagMethodError(MethodDifferentTemplateArgument) << FirstTA << i + 1; + DiagMethodNote(MethodDifferentTemplateArgument) << SecondTA << i + 1; + return true; + } + } + + // Compute the hash of the method as if it has no body. + auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) { + ODRHash Hasher; + Hasher.AddFunctionDecl(D, true /*SkipBody*/); + return Hasher.CalculateHash(); + }; + + // Compare the hash generated to the hash stored. A difference means + // that a body was present in the original source. Due to merging, + // the standard way of detecting a body will not work. + const bool HasFirstBody = + ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash(); + const bool HasSecondBody = + ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); + + if (HasFirstBody != HasSecondBody) { + DiagMethodError(MethodSingleBody) << HasFirstBody; + DiagMethodNote(MethodSingleBody) << HasSecondBody; + return true; + } + + if (HasFirstBody && HasSecondBody) { + DiagMethodError(MethodDifferentBody); + DiagMethodNote(MethodDifferentBody); + return true; + } + + break; + } + + case TypeAlias: + case TypeDef: { + if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, + cast<TypedefNameDecl>(FirstDecl), + cast<TypedefNameDecl>(SecondDecl), + FirstDiffType == TypeAlias)) + return true; + break; + } + case Var: { + if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, + cast<VarDecl>(FirstDecl), + cast<VarDecl>(SecondDecl))) + return true; + break; + } + case Friend: { + const FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl); + const FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl); + + const NamedDecl *FirstND = FirstFriend->getFriendDecl(); + const NamedDecl *SecondND = SecondFriend->getFriendDecl(); + + TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); + TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); + + if (FirstND && SecondND) { + DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), + FriendFunction) + << FirstND; + DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), + FriendFunction) + << SecondND; + return true; + } + + if (FirstTSI && SecondTSI) { + QualType FirstFriendType = FirstTSI->getType(); + QualType SecondFriendType = SecondTSI->getType(); + assert(computeODRHash(FirstFriendType) != + computeODRHash(SecondFriendType)); + DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), + FriendType) + << FirstFriendType; + DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), + FriendType) + << SecondFriendType; + return true; + } + + DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), + FriendTypeFunction) + << (FirstTSI == nullptr); + DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), + FriendTypeFunction) + << (SecondTSI == nullptr); + return true; + } + case FunctionTemplate: { + const FunctionTemplateDecl *FirstTemplate = + cast<FunctionTemplateDecl>(FirstDecl); + const FunctionTemplateDecl *SecondTemplate = + cast<FunctionTemplateDecl>(SecondDecl); + + TemplateParameterList *FirstTPL = FirstTemplate->getTemplateParameters(); + TemplateParameterList *SecondTPL = SecondTemplate->getTemplateParameters(); + + auto DiagTemplateError = [&DiagError, + FirstTemplate](ODRCXXRecordDifference DiffType) { + return DiagError(FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), DiffType) + << FirstTemplate; + }; + auto DiagTemplateNote = [&DiagNote, + SecondTemplate](ODRCXXRecordDifference DiffType) { + return DiagNote(SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), DiffType) + << SecondTemplate; + }; + + if (FirstTPL->size() != SecondTPL->size()) { + DiagTemplateError(FunctionTemplateDifferentNumberParameters) + << FirstTPL->size(); + DiagTemplateNote(FunctionTemplateDifferentNumberParameters) + << SecondTPL->size(); + return true; + } + + for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { + NamedDecl *FirstParam = FirstTPL->getParam(i); + NamedDecl *SecondParam = SecondTPL->getParam(i); + + if (FirstParam->getKind() != SecondParam->getKind()) { + enum { + TemplateTypeParameter, + NonTypeTemplateParameter, + TemplateTemplateParameter, + }; + auto GetParamType = [](NamedDecl *D) { + switch (D->getKind()) { + default: + llvm_unreachable("Unexpected template parameter type"); + case Decl::TemplateTypeParm: + return TemplateTypeParameter; + case Decl::NonTypeTemplateParm: + return NonTypeTemplateParameter; + case Decl::TemplateTemplateParm: + return TemplateTemplateParameter; + } + }; + + DiagTemplateError(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(FirstParam); + DiagTemplateNote(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(SecondParam); + return true; + } + + if (FirstParam->getName() != SecondParam->getName()) { + DiagTemplateError(FunctionTemplateParameterName) + << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; + DiagTemplateNote(FunctionTemplateParameterName) + << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; + return true; + } + + if (isa<TemplateTypeParmDecl>(FirstParam) && + isa<TemplateTypeParmDecl>(SecondParam)) { + TemplateTypeParmDecl *FirstTTPD = + cast<TemplateTypeParmDecl>(FirstParam); + TemplateTypeParmDecl *SecondTTPD = + cast<TemplateTypeParmDecl>(SecondParam); + bool HasFirstDefaultArgument = + FirstTTPD->hasDefaultArgument() && + !FirstTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondTTPD->hasDefaultArgument() && + !SecondTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; + } + + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + QualType FirstType = FirstTTPD->getDefaultArgument(); + QualType SecondType = SecondTTPD->getDefaultArgument(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstType; + DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondType; + return true; + } + } + + if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; + } + } + + if (isa<TemplateTemplateParmDecl>(FirstParam) && + isa<TemplateTemplateParmDecl>(SecondParam)) { + TemplateTemplateParmDecl *FirstTTPD = + cast<TemplateTemplateParmDecl>(FirstParam); + TemplateTemplateParmDecl *SecondTTPD = + cast<TemplateTemplateParmDecl>(SecondParam); + + TemplateParameterList *FirstTPL = FirstTTPD->getTemplateParameters(); + TemplateParameterList *SecondTPL = SecondTTPD->getTemplateParameters(); + + auto ComputeTemplateParameterListODRHash = + [](const TemplateParameterList *TPL) { + assert(TPL); + ODRHash Hasher; + Hasher.AddTemplateParameterList(TPL); + return Hasher.CalculateHash(); + }; + + if (ComputeTemplateParameterListODRHash(FirstTPL) != + ComputeTemplateParameterListODRHash(SecondTPL)) { + DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1); + DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1); + return true; + } + + bool HasFirstDefaultArgument = + FirstTTPD->hasDefaultArgument() && + !FirstTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondTTPD->hasDefaultArgument() && + !SecondTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; + } + + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + TemplateArgument FirstTA = + FirstTTPD->getDefaultArgument().getArgument(); + TemplateArgument SecondTA = + SecondTTPD->getDefaultArgument().getArgument(); + if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) { + DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstTA; + DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondTA; + return true; + } + } + + if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; + } + } + + if (isa<NonTypeTemplateParmDecl>(FirstParam) && + isa<NonTypeTemplateParmDecl>(SecondParam)) { + NonTypeTemplateParmDecl *FirstNTTPD = + cast<NonTypeTemplateParmDecl>(FirstParam); + NonTypeTemplateParmDecl *SecondNTTPD = + cast<NonTypeTemplateParmDecl>(SecondParam); + + QualType FirstType = FirstNTTPD->getType(); + QualType SecondType = SecondNTTPD->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1); + DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1); + return true; + } + + bool HasFirstDefaultArgument = + FirstNTTPD->hasDefaultArgument() && + !FirstNTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondNTTPD->hasDefaultArgument() && + !SecondNTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; + } + + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument(); + Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); + if (computeODRHash(FirstDefaultArgument) != + computeODRHash(SecondDefaultArgument)) { + DiagTemplateError(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondDefaultArgument; + return true; + } + } + + if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstNTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondNTTPD->isParameterPack(); + return true; + } + } + } + break; + } + } + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule.empty() << SecondModule << FirstDiffType + << SecondDecl->getSourceRange(); + return true; +} + +bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord, + const RecordDecl *SecondRecord) const { + if (FirstRecord == SecondRecord) + return false; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + + auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record, + const DeclContext *DC) { + for (const Decl *D : Record->decls()) { + if (!ODRHash::isSubDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + return true; + } + + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, + SecondRecord, SecondModule); + return true; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + // Already handled. + case EndOfClass: + case Other: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + // Cannot be contained by RecordDecl, invalid in this context. + case ObjCMethod: + case ObjCIvar: + case ObjCProperty: + llvm_unreachable("Invalid diff type"); + + case Field: { + if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, + cast<FieldDecl>(FirstDecl), + cast<FieldDecl>(SecondDecl))) + return true; + break; + } + case TypeDef: { + if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, + cast<TypedefNameDecl>(FirstDecl), + cast<TypedefNameDecl>(SecondDecl), + /*IsTypeAlias=*/false)) + return true; + break; + } + case Var: { + if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, + cast<VarDecl>(FirstDecl), + cast<VarDecl>(SecondDecl))) + return true; + break; + } + } + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule.empty() << SecondModule << FirstDiffType + << SecondDecl->getSourceRange(); + return true; +} + +bool ODRDiagsEmitter::diagnoseMismatch( + const FunctionDecl *FirstFunction, + const FunctionDecl *SecondFunction) const { + if (FirstFunction == SecondFunction) + return false; + + // Keep in sync with select options in err_module_odr_violation_function. + enum ODRFunctionDifference { + ReturnType, + ParameterName, + ParameterType, + ParameterSingleDefaultArgument, + ParameterDifferentDefaultArgument, + FunctionBody, + }; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction); + + auto DiagError = [FirstFunction, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_function) + << FirstFunction << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_function) + << SecondModule << Range << DiffType; + }; + + if (computeODRHash(FirstFunction->getReturnType()) != + computeODRHash(SecondFunction->getReturnType())) { + DiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), + FirstFunction->getReturnTypeSourceRange(), ReturnType) + << FirstFunction->getReturnType(); + DiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), + SecondFunction->getReturnTypeSourceRange(), ReturnType) + << SecondFunction->getReturnType(); + return true; + } + + assert(FirstFunction->param_size() == SecondFunction->param_size() && + "Merged functions with different number of parameters"); + + size_t ParamSize = FirstFunction->param_size(); + for (unsigned I = 0; I < ParamSize; ++I) { + const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I); + + assert(Context.hasSameType(FirstParam->getType(), SecondParam->getType()) && + "Merged function has different parameter types."); + + if (FirstParam->getDeclName() != SecondParam->getDeclName()) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterName) + << I + 1 << FirstParam->getDeclName(); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterName) + << I + 1 << SecondParam->getDeclName(); + return true; + }; + + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs<DecayedType>()) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterType) + << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterType) + << (I + 1) << FirstParamType << false; + } + + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs<DecayedType>()) { + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterType) + << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterType) + << (I + 1) << SecondParamType << false; + } + return true; + } + + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + return true; + } + + assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && + "Undiagnosed parameter difference."); + } + + // If no error has been generated before now, assume the problem is in + // the body and generate a message. + DiagError(FirstFunction->getLocation(), FirstFunction->getSourceRange(), + FunctionBody); + DiagNote(SecondFunction->getLocation(), SecondFunction->getSourceRange(), + FunctionBody); + return true; +} + +bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum, + const EnumDecl *SecondEnum) const { + if (FirstEnum == SecondEnum) + return false; + + // Keep in sync with select options in err_module_odr_violation_enum. + enum ODREnumDifference { + SingleScopedEnum, + EnumTagKeywordMismatch, + SingleSpecifiedType, + DifferentSpecifiedTypes, + DifferentNumberEnumConstants, + EnumConstantName, + EnumConstantSingleInitializer, + EnumConstantDifferentInitializer, + }; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum); + + auto DiagError = [FirstEnum, &FirstModule, this](const auto *DiagAnchor, + ODREnumDifference DiffType) { + return Diag(DiagAnchor->getLocation(), diag::err_module_odr_violation_enum) + << FirstEnum << FirstModule.empty() << FirstModule + << DiagAnchor->getSourceRange() << DiffType; + }; + auto DiagNote = [&SecondModule, this](const auto *DiagAnchor, + ODREnumDifference DiffType) { + return Diag(DiagAnchor->getLocation(), diag::note_module_odr_violation_enum) + << SecondModule << DiagAnchor->getSourceRange() << DiffType; + }; + + if (FirstEnum->isScoped() != SecondEnum->isScoped()) { + DiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); + DiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); + return true; + } + + if (FirstEnum->isScoped() && SecondEnum->isScoped()) { + if (FirstEnum->isScopedUsingClassTag() != + SecondEnum->isScopedUsingClassTag()) { + DiagError(FirstEnum, EnumTagKeywordMismatch) + << FirstEnum->isScopedUsingClassTag(); + DiagNote(SecondEnum, EnumTagKeywordMismatch) + << SecondEnum->isScopedUsingClassTag(); + return true; + } + } + + QualType FirstUnderlyingType = + FirstEnum->getIntegerTypeSourceInfo() + ? FirstEnum->getIntegerTypeSourceInfo()->getType() + : QualType(); + QualType SecondUnderlyingType = + SecondEnum->getIntegerTypeSourceInfo() + ? SecondEnum->getIntegerTypeSourceInfo()->getType() + : QualType(); + if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) { + DiagError(FirstEnum, SingleSpecifiedType) << !FirstUnderlyingType.isNull(); + DiagNote(SecondEnum, SingleSpecifiedType) << !SecondUnderlyingType.isNull(); + return true; + } + + if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { + if (computeODRHash(FirstUnderlyingType) != + computeODRHash(SecondUnderlyingType)) { + DiagError(FirstEnum, DifferentSpecifiedTypes) << FirstUnderlyingType; + DiagNote(SecondEnum, DifferentSpecifiedTypes) << SecondUnderlyingType; + return true; + } + } + + // Compare enum constants. + using DeclHashes = + llvm::SmallVector<std::pair<const EnumConstantDecl *, unsigned>, 4>; + auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, const EnumDecl *Enum) { + for (const Decl *D : Enum->decls()) { + // Due to decl merging, the first EnumDecl is the parent of + // Decls in both records. + if (!ODRHash::isSubDeclToBeProcessed(D, FirstEnum)) + continue; + assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind"); + Hashes.emplace_back(cast<EnumConstantDecl>(D), computeODRHash(D)); + } + }; + DeclHashes FirstHashes; + PopulateHashes(FirstHashes, FirstEnum); + DeclHashes SecondHashes; + PopulateHashes(SecondHashes, SecondEnum); + + if (FirstHashes.size() != SecondHashes.size()) { + DiagError(FirstEnum, DifferentNumberEnumConstants) + << (int)FirstHashes.size(); + DiagNote(SecondEnum, DifferentNumberEnumConstants) + << (int)SecondHashes.size(); + return true; + } + + for (unsigned I = 0, N = FirstHashes.size(); I < N; ++I) { + if (FirstHashes[I].second == SecondHashes[I].second) + continue; + const EnumConstantDecl *FirstConstant = FirstHashes[I].first; + const EnumConstantDecl *SecondConstant = SecondHashes[I].first; + + if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) { + DiagError(FirstConstant, EnumConstantName) << I + 1 << FirstConstant; + DiagNote(SecondConstant, EnumConstantName) << I + 1 << SecondConstant; + return true; + } + + const Expr *FirstInit = FirstConstant->getInitExpr(); + const Expr *SecondInit = SecondConstant->getInitExpr(); + if (!FirstInit && !SecondInit) + continue; + + if (!FirstInit || !SecondInit) { + DiagError(FirstConstant, EnumConstantSingleInitializer) + << I + 1 << FirstConstant << (FirstInit != nullptr); + DiagNote(SecondConstant, EnumConstantSingleInitializer) + << I + 1 << SecondConstant << (SecondInit != nullptr); + return true; + } + + if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(FirstConstant, EnumConstantDifferentInitializer) + << I + 1 << FirstConstant; + DiagNote(SecondConstant, EnumConstantDifferentInitializer) + << I + 1 << SecondConstant; + return true; + } + } + return false; +} + +bool ODRDiagsEmitter::diagnoseMismatch( + const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID, + const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstID == SecondID) + return false; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID); + + // Keep in sync with err_module_odr_violation_objc_interface. + enum ODRInterfaceDifference { + SuperClassType, + IVarAccess, + }; + + auto DiagError = [FirstID, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRInterfaceDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_objc_interface) + << FirstID << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range, + ODRInterfaceDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_objc_interface) + << SecondModule.empty() << SecondModule << Range << DiffType; + }; + + const struct ObjCInterfaceDecl::DefinitionData *FirstDD = &FirstID->data(); + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + if (FirstDD != SecondDD) { + // Check for matching super class. + auto GetSuperClassSourceRange = [](const TypeSourceInfo *SuperInfo, + const ObjCInterfaceDecl *ID) { + if (!SuperInfo) + return ID->getSourceRange(); + TypeLoc Loc = SuperInfo->getTypeLoc(); + return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc()); + }; + + ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass(); + ObjCInterfaceDecl *SecondSuperClass = nullptr; + const TypeSourceInfo *FirstSuperInfo = FirstID->getSuperClassTInfo(); + const TypeSourceInfo *SecondSuperInfo = SecondDD->SuperClassTInfo; + if (SecondSuperInfo) + SecondSuperClass = + SecondSuperInfo->getType()->castAs<ObjCObjectType>()->getInterface(); + + if ((FirstSuperClass && SecondSuperClass && + FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) || + (FirstSuperClass && !SecondSuperClass) || + (!FirstSuperClass && SecondSuperClass)) { + QualType FirstType; + if (FirstSuperInfo) + FirstType = FirstSuperInfo->getType(); + + DiagError(FirstID->getLocation(), + GetSuperClassSourceRange(FirstSuperInfo, FirstID), + SuperClassType) + << (bool)FirstSuperInfo << FirstType; + + QualType SecondType; + if (SecondSuperInfo) + SecondType = SecondSuperInfo->getType(); + + DiagNote(SecondID->getLocation(), + GetSuperClassSourceRange(SecondSuperInfo, SecondID), + SuperClassType) + << (bool)SecondSuperInfo << SecondType; + return true; + } + + // Check both interfaces reference the same protocols. + auto &FirstProtos = FirstID->getReferencedProtocols(); + auto &SecondProtos = SecondDD->ReferencedProtocols; + if (diagnoseSubMismatchProtocols(FirstProtos, FirstID, FirstModule, + SecondProtos, SecondID, SecondModule)) + return true; + } + + auto PopulateHashes = [](DeclHashes &Hashes, const ObjCInterfaceDecl *ID, + const DeclContext *DC) { + for (auto *D : ID->decls()) { + if (!ODRHash::isSubDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + // Use definition as DeclContext because definitions are merged when + // DeclContexts are merged and separate when DeclContexts are separate. + PopulateHashes(FirstHashes, FirstID, FirstID->getDefinition()); + PopulateHashes(SecondHashes, SecondID, SecondID->getDefinition()); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstID, FirstModule, SecondID, + SecondModule); + return true; + } + + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstID, FirstModule, SecondID, + SecondModule); + return true; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + // Already handled. + case EndOfClass: + case Other: + // Cannot be contained by ObjCInterfaceDecl, invalid in this context. + case Field: + case TypeDef: + case Var: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + + case ObjCMethod: { + if (diagnoseSubMismatchObjCMethod(FirstID, FirstModule, SecondModule, + cast<ObjCMethodDecl>(FirstDecl), + cast<ObjCMethodDecl>(SecondDecl))) + return true; + break; + } + case ObjCIvar: { + if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule, + cast<FieldDecl>(FirstDecl), + cast<FieldDecl>(SecondDecl))) + return true; + + // Check if the access match. + const ObjCIvarDecl *FirstIvar = cast<ObjCIvarDecl>(FirstDecl); + const ObjCIvarDecl *SecondIvar = cast<ObjCIvarDecl>(SecondDecl); + if (FirstIvar->getCanonicalAccessControl() != + SecondIvar->getCanonicalAccessControl()) { + DiagError(FirstIvar->getLocation(), FirstIvar->getSourceRange(), + IVarAccess) + << FirstIvar->getName() + << (int)FirstIvar->getCanonicalAccessControl(); + DiagNote(SecondIvar->getLocation(), SecondIvar->getSourceRange(), + IVarAccess) + << SecondIvar->getName() + << (int)SecondIvar->getCanonicalAccessControl(); + return true; + } + break; + } + case ObjCProperty: { + if (diagnoseSubMismatchObjCProperty(FirstID, FirstModule, SecondModule, + cast<ObjCPropertyDecl>(FirstDecl), + cast<ObjCPropertyDecl>(SecondDecl))) + return true; + break; + } + } + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstID << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + return true; +} + +bool ODRDiagsEmitter::diagnoseMismatch( + const ObjCProtocolDecl *FirstProtocol, + const ObjCProtocolDecl *SecondProtocol, + const struct ObjCProtocolDecl::DefinitionData *SecondDD) const { + if (FirstProtocol == SecondProtocol) + return false; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstProtocol); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondProtocol); + + const ObjCProtocolDecl::DefinitionData *FirstDD = &FirstProtocol->data(); + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + // Diagnostics from ObjCProtocol DefinitionData are emitted here. + if (FirstDD != SecondDD) { + // Check both protocols reference the same protocols. + const ObjCProtocolList &FirstProtocols = + FirstProtocol->getReferencedProtocols(); + const ObjCProtocolList &SecondProtocols = SecondDD->ReferencedProtocols; + if (diagnoseSubMismatchProtocols(FirstProtocols, FirstProtocol, FirstModule, + SecondProtocols, SecondProtocol, + SecondModule)) + return true; + } + + auto PopulateHashes = [](DeclHashes &Hashes, const ObjCProtocolDecl *ID, + const DeclContext *DC) { + for (const Decl *D : ID->decls()) { + if (!ODRHash::isSubDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + // Use definition as DeclContext because definitions are merged when + // DeclContexts are merged and separate when DeclContexts are separate. + PopulateHashes(FirstHashes, FirstProtocol, FirstProtocol->getDefinition()); + PopulateHashes(SecondHashes, SecondProtocol, SecondProtocol->getDefinition()); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstProtocol, FirstModule, + SecondProtocol, SecondModule); + return true; + } + + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstProtocol, FirstModule, + SecondProtocol, SecondModule); + return true; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + // Already handled. + case EndOfClass: + case Other: + // Cannot be contained by ObjCProtocolDecl, invalid in this context. + case Field: + case TypeDef: + case Var: + case ObjCIvar: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + case ObjCMethod: { + if (diagnoseSubMismatchObjCMethod(FirstProtocol, FirstModule, SecondModule, + cast<ObjCMethodDecl>(FirstDecl), + cast<ObjCMethodDecl>(SecondDecl))) + return true; + break; + } + case ObjCProperty: { + if (diagnoseSubMismatchObjCProperty(FirstProtocol, FirstModule, + SecondModule, + cast<ObjCPropertyDecl>(FirstDecl), + cast<ObjCPropertyDecl>(SecondDecl))) + return true; + break; + } + } + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstProtocol << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule.empty() << SecondModule << FirstDiffType + << SecondDecl->getSourceRange(); + return true; +} diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 04cbb09356d7..3374b49f5d8e 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -72,7 +72,10 @@ void ODRHash::AddDeclarationNameImpl(DeclarationName Name) { AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); ID.AddInteger(NumArgs); - for (unsigned i = 0; i < NumArgs; ++i) { + // Compare all selector slots. For selectors with arguments it means all arg + // slots. And if there are no arguments, compare the first-and-only slot. + unsigned SlotsToCheck = NumArgs > 0 ? NumArgs : 1; + for (unsigned i = 0; i < SlotsToCheck; ++i) { const IdentifierInfo *II = S.getIdentifierInfoForSlot(i); AddBoolean(II); if (II) { @@ -169,8 +172,14 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { AddDecl(TA.getAsDecl()); break; case TemplateArgument::NullPtr: - case TemplateArgument::Integral: + ID.AddPointer(nullptr); break; + case TemplateArgument::Integral: { + // There are integrals (e.g.: _BitInt(128)) that cannot be represented as + // any builtin integral type, so we use the hash of APSInt instead. + TA.getAsIntegral().Profile(ID); + break; + } case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: AddTemplateName(TA.getAsTemplateOrTemplatePattern()); @@ -334,6 +343,20 @@ public: Inherited::VisitFieldDecl(D); } + void VisitObjCIvarDecl(const ObjCIvarDecl *D) { + ID.AddInteger(D->getCanonicalAccessControl()); + Inherited::VisitObjCIvarDecl(D); + } + + void VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + ID.AddInteger(D->getPropertyAttributes()); + ID.AddInteger(D->getPropertyImplementation()); + AddQualType(D->getType()); + AddDecl(D); + + Inherited::VisitObjCPropertyDecl(D); + } + void VisitFunctionDecl(const FunctionDecl *D) { // Handled by the ODRHash for FunctionDecl ID.AddInteger(D->getODRHash()); @@ -347,6 +370,64 @@ public: Inherited::VisitCXXMethodDecl(D); } + void VisitObjCMethodDecl(const ObjCMethodDecl *Method) { + ID.AddInteger(Method->getDeclKind()); + Hash.AddBoolean(Method->isInstanceMethod()); // false if class method + Hash.AddBoolean(Method->isPropertyAccessor()); + Hash.AddBoolean(Method->isVariadic()); + Hash.AddBoolean(Method->isSynthesizedAccessorStub()); + Hash.AddBoolean(Method->isDefined()); + Hash.AddBoolean(Method->isOverriding()); + Hash.AddBoolean(Method->isDirectMethod()); + Hash.AddBoolean(Method->isThisDeclarationADesignatedInitializer()); + Hash.AddBoolean(Method->hasSkippedBody()); + + ID.AddInteger(Method->getImplementationControl()); + ID.AddInteger(Method->getMethodFamily()); + ImplicitParamDecl *Cmd = Method->getCmdDecl(); + Hash.AddBoolean(Cmd); + if (Cmd) + ID.AddInteger(Cmd->getParameterKind()); + + ImplicitParamDecl *Self = Method->getSelfDecl(); + Hash.AddBoolean(Self); + if (Self) + ID.AddInteger(Self->getParameterKind()); + + AddDecl(Method); + + AddQualType(Method->getReturnType()); + ID.AddInteger(Method->param_size()); + for (auto Param : Method->parameters()) + Hash.AddSubDecl(Param); + + if (Method->hasBody()) { + const bool IsDefinition = Method->isThisDeclarationADefinition(); + Hash.AddBoolean(IsDefinition); + if (IsDefinition) { + Stmt *Body = Method->getBody(); + Hash.AddBoolean(Body); + if (Body) + AddStmt(Body); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : Method->decls()) + if (ODRHash::isSubDeclToBeProcessed(SubDecl, Method)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) + Hash.AddSubDecl(SubDecl); + } + } else { + Hash.AddBoolean(false); + } + + Inherited::VisitObjCMethodDecl(Method); + } + void VisitTypedefNameDecl(const TypedefNameDecl *D) { AddQualType(D->getUnderlyingType()); @@ -441,7 +522,7 @@ public: // Only allow a small portion of Decl's to be processed. Remove this once // all Decl's can be handled. -bool ODRHash::isDeclToBeProcessed(const Decl *D, const DeclContext *Parent) { +bool ODRHash::isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent) { if (D->isImplicit()) return false; if (D->getDeclContext() != Parent) return false; @@ -460,6 +541,9 @@ bool ODRHash::isDeclToBeProcessed(const Decl *D, const DeclContext *Parent) { case Decl::TypeAlias: case Decl::Typedef: case Decl::Var: + case Decl::ObjCMethod: + case Decl::ObjCIvar: + case Decl::ObjCProperty: return true; } } @@ -488,7 +572,7 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { // accurate count of Decl's. llvm::SmallVector<const Decl *, 16> Decls; for (Decl *SubDecl : Record->decls()) { - if (isDeclToBeProcessed(SubDecl, Record)) { + if (isSubDeclToBeProcessed(SubDecl, Record)) { Decls.push_back(SubDecl); if (auto *Function = dyn_cast<FunctionDecl>(SubDecl)) { // Compute/Preload ODRHash into FunctionDecl. @@ -517,6 +601,51 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { } } +void ODRHash::AddRecordDecl(const RecordDecl *Record) { + assert(!isa<CXXRecordDecl>(Record) && + "For CXXRecordDecl should call AddCXXRecordDecl."); + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : Record->decls()) { + if (isSubDeclToBeProcessed(SubDecl, Record)) + Decls.push_back(SubDecl); + } + + ID.AddInteger(Decls.size()); + for (const Decl *SubDecl : Decls) + AddSubDecl(SubDecl); +} + +void ODRHash::AddObjCInterfaceDecl(const ObjCInterfaceDecl *IF) { + AddDecl(IF); + + auto *SuperClass = IF->getSuperClass(); + AddBoolean(SuperClass); + if (SuperClass) + ID.AddInteger(SuperClass->getODRHash()); + + // Hash referenced protocols. + ID.AddInteger(IF->getReferencedProtocols().size()); + for (const ObjCProtocolDecl *RefP : IF->protocols()) { + // Hash the name only as a referenced protocol can be a forward declaration. + AddDeclarationName(RefP->getDeclName()); + } + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : IF->decls()) + if (isSubDeclToBeProcessed(SubDecl, IF)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto *SubDecl : Decls) + AddSubDecl(SubDecl); +} + void ODRHash::AddFunctionDecl(const FunctionDecl *Function, bool SkipBody) { assert(Function && "Expecting non-null pointer."); @@ -564,7 +693,7 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function, AddQualType(Function->getReturnType()); ID.AddInteger(Function->param_size()); - for (auto Param : Function->parameters()) + for (auto *Param : Function->parameters()) AddSubDecl(Param); if (SkipBody) { @@ -589,7 +718,7 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function, // accurate count of Decl's. llvm::SmallVector<const Decl *, 16> Decls; for (Decl *SubDecl : Function->decls()) { - if (isDeclToBeProcessed(SubDecl, Function)) { + if (isSubDeclToBeProcessed(SubDecl, Function)) { Decls.push_back(SubDecl); } } @@ -615,7 +744,7 @@ void ODRHash::AddEnumDecl(const EnumDecl *Enum) { // accurate count of Decl's. llvm::SmallVector<const Decl *, 16> Decls; for (Decl *SubDecl : Enum->decls()) { - if (isDeclToBeProcessed(SubDecl, Enum)) { + if (isSubDeclToBeProcessed(SubDecl, Enum)) { assert(isa<EnumConstantDecl>(SubDecl) && "Unexpected Decl"); Decls.push_back(SubDecl); } @@ -628,6 +757,31 @@ void ODRHash::AddEnumDecl(const EnumDecl *Enum) { } +void ODRHash::AddObjCProtocolDecl(const ObjCProtocolDecl *P) { + AddDecl(P); + + // Hash referenced protocols. + ID.AddInteger(P->getReferencedProtocols().size()); + for (const ObjCProtocolDecl *RefP : P->protocols()) { + // Hash the name only as a referenced protocol can be a forward declaration. + AddDeclarationName(RefP->getDeclName()); + } + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : P->decls()) { + if (isSubDeclToBeProcessed(SubDecl, P)) { + Decls.push_back(SubDecl); + } + } + + ID.AddInteger(Decls.size()); + for (auto *SubDecl : Decls) { + AddSubDecl(SubDecl); + } +} + void ODRHash::AddDecl(const Decl *D) { assert(D && "Expecting non-null pointer."); D = D->getCanonicalDecl(); @@ -671,7 +825,7 @@ public: } } - void AddDecl(Decl *D) { + void AddDecl(const Decl *D) { Hash.AddBoolean(D); if (D) { Hash.AddDecl(D); @@ -861,7 +1015,7 @@ public: ID.AddInteger(T->isConstrained()); if (T->isConstrained()) { AddDecl(T->getTypeConstraintConcept()); - ID.AddInteger(T->getNumArgs()); + ID.AddInteger(T->getTypeConstraintArguments().size()); for (const auto &TA : T->getTypeConstraintArguments()) Hash.AddTemplateArgument(TA); } @@ -934,7 +1088,7 @@ public: auto Protocols = T->getProtocols(); ID.AddInteger(Protocols.size()); - for (auto Protocol : Protocols) { + for (auto *Protocol : Protocols) { AddDecl(Protocol); } @@ -952,7 +1106,7 @@ public: AddDecl(T->getDecl()); auto Protocols = T->getProtocols(); ID.AddInteger(Protocols.size()); - for (auto Protocol : Protocols) { + for (auto *Protocol : Protocols) { AddDecl(Protocol); } @@ -995,13 +1149,13 @@ public: void VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) { - AddType(T->getReplacedParameter()); + AddDecl(T->getAssociatedDecl()); Hash.AddTemplateArgument(T->getArgumentPack()); VisitType(T); } void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { - AddType(T->getReplacedParameter()); + AddDecl(T->getAssociatedDecl()); AddQualType(T->getReplacementType()); VisitType(T); } @@ -1015,7 +1169,7 @@ public: void VisitEnumType(const EnumType *T) { VisitTagType(T); } void VisitTemplateSpecializationType(const TemplateSpecializationType *T) { - ID.AddInteger(T->getNumArgs()); + ID.AddInteger(T->template_arguments().size()); for (const auto &TA : T->template_arguments()) { Hash.AddTemplateArgument(TA); } @@ -1061,7 +1215,7 @@ public: VisitType(T); } void VisitTypeOfType(const TypeOfType *T) { - AddQualType(T->getUnderlyingType()); + AddQualType(T->getUnmodifiedType()); VisitType(T); } @@ -1080,7 +1234,7 @@ public: const DependentTemplateSpecializationType *T) { AddIdentifierInfo(T->getIdentifier()); AddNestedNameSpecifier(T->getQualifier()); - ID.AddInteger(T->getNumArgs()); + ID.AddInteger(T->template_arguments().size()); for (const auto &TA : T->template_arguments()) { Hash.AddTemplateArgument(TA); } diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp index 40fa8c3802c3..5e320416b30d 100644 --- a/clang/lib/AST/OSLog.cpp +++ b/clang/lib/AST/OSLog.cpp @@ -8,6 +8,7 @@ #include "clang/AST/FormatString.h" #include "clang/Basic/Builtins.h" #include "llvm/ADT/SmallBitVector.h" +#include <optional> using namespace clang; @@ -20,11 +21,11 @@ class OSLogFormatStringHandler private: struct ArgData { const Expr *E = nullptr; - Optional<OSLogBufferItem::Kind> Kind; - Optional<unsigned> Size; - Optional<const Expr *> Count; - Optional<const Expr *> Precision; - Optional<const Expr *> FieldWidth; + std::optional<OSLogBufferItem::Kind> Kind; + std::optional<unsigned> Size; + std::optional<const Expr *> Count; + std::optional<const Expr *> Precision; + std::optional<const Expr *> FieldWidth; unsigned char Flags = 0; StringRef MaskType; }; diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index dc2d90e366bc..2e88c08ae789 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/ErrorHandling.h" #include <algorithm> #include <cassert> +#include <optional> using namespace clang; using namespace llvm; @@ -102,6 +103,8 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { return static_cast<const OMPNocontextClause *>(C); case OMPC_filter: return static_cast<const OMPFilterClause *>(C); + case OMPC_ompx_dyn_cgroup_mem: + return static_cast<const OMPXDynCGroupMemClause *>(C); case OMPC_default: case OMPC_proc_bind: case OMPC_safelen: @@ -152,6 +155,9 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_at: + case OMPC_severity: + case OMPC_message: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -251,6 +257,9 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_at: + case OMPC_severity: + case OMPC_message: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -307,7 +316,7 @@ OMPClause::child_range OMPNumTasksClause::used_children() { OMPClause::child_range OMPFinalClause::used_children() { if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) return child_range(C, C + 1); - return child_range(&Condition, &Condition + 1); + return children(); } OMPClause::child_range OMPPriorityClause::used_children() { @@ -319,13 +328,13 @@ OMPClause::child_range OMPPriorityClause::used_children() { OMPClause::child_range OMPNovariantsClause::used_children() { if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) return child_range(C, C + 1); - return child_range(&Condition, &Condition + 1); + return children(); } OMPClause::child_range OMPNocontextClause::used_children() { if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) return child_range(C, C + 1); - return child_range(&Condition, &Condition + 1); + return children(); } OMPOrderedClause *OMPOrderedClause::Create(const ASTContext &C, Expr *Num, @@ -361,7 +370,7 @@ void OMPOrderedClause::setLoopNumIterations(unsigned NumLoop, } ArrayRef<Expr *> OMPOrderedClause::getLoopNumIterations() const { - return llvm::makeArrayRef(getTrailingObjects<Expr *>(), NumberOfLoops); + return llvm::ArrayRef(getTrailingObjects<Expr *>(), NumberOfLoops); } void OMPOrderedClause::setLoopCounter(unsigned NumLoop, Expr *Counter) { @@ -1127,7 +1136,7 @@ OMPMapClause *OMPMapClause::Create( const ASTContext &C, const OMPVarListLocTy &Locs, ArrayRef<Expr *> Vars, ArrayRef<ValueDecl *> Declarations, MappableExprComponentListsRef ComponentLists, ArrayRef<Expr *> UDMapperRefs, - ArrayRef<OpenMPMapModifierKind> MapModifiers, + Expr *IteratorModifier, ArrayRef<OpenMPMapModifierKind> MapModifiers, ArrayRef<SourceLocation> MapModifiersLoc, NestedNameSpecifierLoc UDMQualifierLoc, DeclarationNameInfo MapperId, OpenMPMapClauseKind Type, bool TypeIsImplicit, SourceLocation TypeLoc) { @@ -1150,7 +1159,7 @@ OMPMapClause *OMPMapClause::Create( void *Mem = C.Allocate( totalSizeToAlloc<Expr *, ValueDecl *, unsigned, OMPClauseMappableExprCommon::MappableComponent>( - 2 * Sizes.NumVars, Sizes.NumUniqueDeclarations, + 2 * Sizes.NumVars + 1, Sizes.NumUniqueDeclarations, Sizes.NumUniqueDeclarations + Sizes.NumComponentLists, Sizes.NumComponents)); OMPMapClause *Clause = new (Mem) @@ -1159,6 +1168,7 @@ OMPMapClause *OMPMapClause::Create( Clause->setVarRefs(Vars); Clause->setUDMapperRefs(UDMapperRefs); + Clause->setIteratorModifier(IteratorModifier); Clause->setClauseInfo(Declarations, ComponentLists); Clause->setMapType(Type); Clause->setMapLoc(TypeLoc); @@ -1171,10 +1181,12 @@ OMPMapClause::CreateEmpty(const ASTContext &C, void *Mem = C.Allocate( totalSizeToAlloc<Expr *, ValueDecl *, unsigned, OMPClauseMappableExprCommon::MappableComponent>( - 2 * Sizes.NumVars, Sizes.NumUniqueDeclarations, + 2 * Sizes.NumVars + 1, Sizes.NumUniqueDeclarations, Sizes.NumUniqueDeclarations + Sizes.NumComponentLists, Sizes.NumComponents)); - return new (Mem) OMPMapClause(Sizes); + OMPMapClause *Clause = new (Mem) OMPMapClause(Sizes); + Clause->setIteratorModifier(nullptr); + return Clause; } OMPToClause *OMPToClause::Create( @@ -1626,18 +1638,19 @@ OMPAffinityClause *OMPAffinityClause::CreateEmpty(const ASTContext &C, } OMPInitClause *OMPInitClause::Create(const ASTContext &C, Expr *InteropVar, - ArrayRef<Expr *> PrefExprs, bool IsTarget, - bool IsTargetSync, SourceLocation StartLoc, + OMPInteropInfo &InteropInfo, + SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation VarLoc, SourceLocation EndLoc) { - void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(PrefExprs.size() + 1)); - auto *Clause = - new (Mem) OMPInitClause(IsTarget, IsTargetSync, StartLoc, LParenLoc, - VarLoc, EndLoc, PrefExprs.size() + 1); + void *Mem = + C.Allocate(totalSizeToAlloc<Expr *>(InteropInfo.PreferTypes.size() + 1)); + auto *Clause = new (Mem) OMPInitClause( + InteropInfo.IsTarget, InteropInfo.IsTargetSync, StartLoc, LParenLoc, + VarLoc, EndLoc, InteropInfo.PreferTypes.size() + 1); Clause->setInteropVar(InteropVar); - llvm::copy(PrefExprs, Clause->getTrailingObjects<Expr *>() + 1); + llvm::copy(InteropInfo.PreferTypes, Clause->getTrailingObjects<Expr *>() + 1); return Clause; } @@ -1701,7 +1714,7 @@ void OMPClausePrinter::VisitOMPSimdlenClause(OMPSimdlenClause *Node) { void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { OS << "sizes("; bool First = true; - for (auto Size : Node->getSizesRefs()) { + for (auto *Size : Node->getSizesRefs()) { if (!First) OS << ", "; Size->printPretty(OS, nullptr, Policy, 0); @@ -1780,6 +1793,22 @@ void OMPClausePrinter::VisitOMPAtomicDefaultMemOrderClause( << ")"; } +void OMPClausePrinter::VisitOMPAtClause(OMPAtClause *Node) { + OS << "at(" << getOpenMPSimpleClauseTypeName(OMPC_at, Node->getAtKind()) + << ")"; +} + +void OMPClausePrinter::VisitOMPSeverityClause(OMPSeverityClause *Node) { + OS << "severity(" + << getOpenMPSimpleClauseTypeName(OMPC_severity, Node->getSeverityKind()) + << ")"; +} + +void OMPClausePrinter::VisitOMPMessageClause(OMPMessageClause *Node) { + OS << "message(\"" + << cast<StringLiteral>(Node->getMessageString())->getString() << "\")"; +} + void OMPClausePrinter::VisitOMPScheduleClause(OMPScheduleClause *Node) { OS << "schedule("; if (Node->getFirstScheduleModifier() != OMPC_SCHEDULE_MODIFIER_unknown) { @@ -1904,12 +1933,22 @@ void OMPClausePrinter::VisitOMPPriorityClause(OMPPriorityClause *Node) { void OMPClausePrinter::VisitOMPGrainsizeClause(OMPGrainsizeClause *Node) { OS << "grainsize("; + OpenMPGrainsizeClauseModifier Modifier = Node->getModifier(); + if (Modifier != OMPC_GRAINSIZE_unknown) { + OS << getOpenMPSimpleClauseTypeName(Node->getClauseKind(), Modifier) + << ": "; + } Node->getGrainsize()->printPretty(OS, nullptr, Policy, 0); OS << ")"; } void OMPClausePrinter::VisitOMPNumTasksClause(OMPNumTasksClause *Node) { OS << "num_tasks("; + OpenMPNumTasksClauseModifier Modifier = Node->getModifier(); + if (Modifier != OMPC_NUMTASKS_unknown) { + OS << getOpenMPSimpleClauseTypeName(Node->getClauseKind(), Modifier) + << ": "; + } Node->getNumTasks()->printPretty(OS, nullptr, Policy, 0); OS << ")"; } @@ -2215,16 +2254,27 @@ static void PrintMapper(raw_ostream &OS, T *Node, OS << Node->getMapperIdInfo() << ')'; } +template <typename T> +static void PrintIterator(raw_ostream &OS, T *Node, + const PrintingPolicy &Policy) { + if (Expr *IteratorModifier = Node->getIteratorModifier()) + IteratorModifier->printPretty(OS, nullptr, Policy); +} + void OMPClausePrinter::VisitOMPMapClause(OMPMapClause *Node) { if (!Node->varlist_empty()) { OS << "map("; if (Node->getMapType() != OMPC_MAP_unknown) { for (unsigned I = 0; I < NumberOfOMPMapClauseModifiers; ++I) { if (Node->getMapTypeModifier(I) != OMPC_MAP_MODIFIER_unknown) { - OS << getOpenMPSimpleClauseTypeName(OMPC_map, - Node->getMapTypeModifier(I)); - if (Node->getMapTypeModifier(I) == OMPC_MAP_MODIFIER_mapper) - PrintMapper(OS, Node, Policy); + if (Node->getMapTypeModifier(I) == OMPC_MAP_MODIFIER_iterator) { + PrintIterator(OS, Node, Policy); + } else { + OS << getOpenMPSimpleClauseTypeName(OMPC_map, + Node->getMapTypeModifier(I)); + if (Node->getMapTypeModifier(I) == OMPC_MAP_MODIFIER_mapper) + PrintMapper(OS, Node, Policy); + } OS << ','; } } @@ -2337,8 +2387,12 @@ void OMPClausePrinter::VisitOMPNontemporalClause(OMPNontemporalClause *Node) { } void OMPClausePrinter::VisitOMPOrderClause(OMPOrderClause *Node) { - OS << "order(" << getOpenMPSimpleClauseTypeName(OMPC_order, Node->getKind()) - << ")"; + OS << "order("; + if (Node->getModifier() != OMPC_ORDER_MODIFIER_unknown) { + OS << getOpenMPSimpleClauseTypeName(OMPC_order, Node->getModifier()); + OS << ": "; + } + OS << getOpenMPSimpleClauseTypeName(OMPC_order, Node->getKind()) << ")"; } void OMPClausePrinter::VisitOMPInclusiveClause(OMPInclusiveClause *Node) { @@ -2403,6 +2457,13 @@ void OMPClausePrinter::VisitOMPBindClause(OMPBindClause *Node) { << ")"; } +void OMPClausePrinter::VisitOMPXDynCGroupMemClause( + OMPXDynCGroupMemClause *Node) { + OS << "ompx_dyn_cgroup_mem("; + Node->getSize()->printPretty(OS, nullptr, Policy, 0); + OS << ")"; +} + void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx, VariantMatchInfo &VMI) const { for (const OMPTraitSet &Set : Sets) { @@ -2417,7 +2478,7 @@ void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx, TraitProperty::user_condition_unknown && "Ill-formed user condition, expected unknown trait property!"); - if (Optional<APSInt> CondVal = + if (std::optional<APSInt> CondVal = Selector.ScoreOrCondition->getIntegerConstantExpr(ASTCtx)) VMI.addTrait(CondVal->isZero() ? TraitProperty::user_condition_false : TraitProperty::user_condition_true, @@ -2427,7 +2488,7 @@ void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx, continue; } - Optional<llvm::APSInt> Score; + std::optional<llvm::APSInt> Score; llvm::APInt *ScorePtr = nullptr; if (Selector.ScoreOrCondition) { if ((Score = Selector.ScoreOrCondition->getIntegerConstantExpr(ASTCtx))) diff --git a/clang/lib/AST/ParentMap.cpp b/clang/lib/AST/ParentMap.cpp index da21e573c320..3d6a1cc84c7b 100644 --- a/clang/lib/AST/ParentMap.cpp +++ b/clang/lib/AST/ParentMap.cpp @@ -33,9 +33,11 @@ static void BuildParentMap(MapTy& M, Stmt* S, switch (S->getStmtClass()) { case Stmt::PseudoObjectExprClass: { - assert(OVMode == OV_Transparent && "Should not appear alongside OVEs"); PseudoObjectExpr *POE = cast<PseudoObjectExpr>(S); + if (OVMode == OV_Opaque && M[POE->getSyntacticForm()]) + break; + // If we are rebuilding the map, clear out any existing state. if (M[POE->getSyntacticForm()]) for (Stmt *SubStmt : S->children()) diff --git a/clang/lib/AST/ParentMapContext.cpp b/clang/lib/AST/ParentMapContext.cpp index e0d4700e4b10..21cfd5b1de6e 100644 --- a/clang/lib/AST/ParentMapContext.cpp +++ b/clang/lib/AST/ParentMapContext.cpp @@ -99,7 +99,7 @@ class ParentMapContext::ParentMap { return llvm::ArrayRef<DynTypedNode>(); } if (const auto *V = I->second.template dyn_cast<ParentVector *>()) { - return llvm::makeArrayRef(*V); + return llvm::ArrayRef(*V); } return getSingleDynTypedNodeFromParentMap(I->second); } @@ -252,7 +252,7 @@ public: const auto *S = It->second.dyn_cast<const Stmt *>(); if (!S) { if (auto *Vec = It->second.dyn_cast<ParentVector *>()) - return llvm::makeArrayRef(*Vec); + return llvm::ArrayRef(*Vec); return getSingleDynTypedNodeFromParentMap(It->second); } const auto *P = dyn_cast<Expr>(S); @@ -265,16 +265,6 @@ public: } }; -template <typename Tuple, std::size_t... Is> -auto tuple_pop_front_impl(const Tuple &tuple, std::index_sequence<Is...>) { - return std::make_tuple(std::get<1 + Is>(tuple)...); -} - -template <typename Tuple> auto tuple_pop_front(const Tuple &tuple) { - return tuple_pop_front_impl( - tuple, std::make_index_sequence<std::tuple_size<Tuple>::value - 1>()); -} - template <typename T, typename... U> struct MatchParents { static std::tuple<bool, DynTypedNodeList, const T *, const U *...> match(const DynTypedNodeList &NodeList, @@ -285,10 +275,11 @@ template <typename T, typename... U> struct MatchParents { if (NextParentList.size() == 1) { auto TailTuple = MatchParents<U...>::match(NextParentList, ParentMap); if (std::get<bool>(TailTuple)) { - return std::tuple_cat( - std::make_tuple(true, std::get<DynTypedNodeList>(TailTuple), - TypedNode), - tuple_pop_front(tuple_pop_front(TailTuple))); + return std::apply( + [TypedNode](bool, DynTypedNodeList NodeList, auto... TupleTail) { + return std::make_tuple(true, NodeList, TypedNode, TupleTail...); + }, + TailTuple); } } } diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index c6c41abc7e9a..96079c235c5e 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -326,6 +326,14 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, case 's': k = ConversionSpecifier::sArg; break; case 'u': k = ConversionSpecifier::uArg; break; case 'x': k = ConversionSpecifier::xArg; break; + // C23. + case 'b': + if (isFreeBSDKPrintf) + k = ConversionSpecifier::FreeBSDbArg; // int followed by char * + else + k = ConversionSpecifier::bArg; + break; + case 'B': k = ConversionSpecifier::BArg; break; // POSIX specific. case 'C': k = ConversionSpecifier::CArg; break; case 'S': k = ConversionSpecifier::SArg; break; @@ -337,11 +345,6 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. case 'm': k = ConversionSpecifier::PrintErrno; break; - // FreeBSD kernel specific. - case 'b': - if (isFreeBSDKPrintf) - k = ConversionSpecifier::FreeBSDbArg; // int followed by char * - break; case 'r': if (isFreeBSDKPrintf) k = ConversionSpecifier::FreeBSDrArg; // int @@ -497,7 +500,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, case LengthModifier::AsShort: if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) return Ctx.IntTy; - LLVM_FALLTHROUGH; + [[fallthrough]]; default: return ArgType::Invalid(); } @@ -844,7 +847,7 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, } // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) + if (LangOpt.C99 || LangOpt.CPlusPlus11) namedTypeToLengthModifier(QT, LM); // If fixing the length modifier was enough, we might be done. @@ -874,7 +877,7 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, // Set conversion specifier and disable any flags which do not apply to it. // Let typedefs to char fall through to int, as %c is silly for uint8_t. - if (!isa<TypedefType>(QT) && QT->isCharType()) { + if (!QT->getAs<TypedefType>() && QT->isCharType()) { CS.setKind(ConversionSpecifier::cArg); LM.setKind(LengthModifier::None); Precision.setHowSpecified(OptionalAmount::NotSpecified); @@ -885,12 +888,10 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, // Test for Floating type first as LongDouble can pass isUnsignedIntegerType else if (QT->isRealFloatingType()) { CS.setKind(ConversionSpecifier::fArg); - } - else if (QT->isSignedIntegerType()) { + } else if (QT->isSignedIntegerType()) { CS.setKind(ConversionSpecifier::dArg); HasAlternativeForm = false; - } - else if (QT->isUnsignedIntegerType()) { + } else if (QT->isUnsignedIntegerType()) { CS.setKind(ConversionSpecifier::uArg); HasAlternativeForm = false; HasPlusPrefix = false; @@ -963,8 +964,10 @@ bool PrintfSpecifier::hasValidAlternativeForm() const { if (!HasAlternativeForm) return true; - // Alternate form flag only valid with the oxXaAeEfFgG conversions + // Alternate form flag only valid with the bBoxXaAeEfFgG conversions switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::oArg: case ConversionSpecifier::OArg: case ConversionSpecifier::xArg: @@ -990,8 +993,10 @@ bool PrintfSpecifier::hasValidLeadingZeros() const { if (!HasLeadingZeroes) return true; - // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions + // Leading zeroes flag only valid with the bBdiouxXaAeEfFgG conversions switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: @@ -1082,8 +1087,10 @@ bool PrintfSpecifier::hasValidPrecision() const { if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) return true; - // Precision is only valid with the diouxXaAeEfFgGsP conversions + // Precision is only valid with the bBdiouxXaAeEfFgGsP conversions switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: diff --git a/clang/lib/AST/QualTypeNames.cpp b/clang/lib/AST/QualTypeNames.cpp index 26aaa96a1dc6..7557336f0aaf 100644 --- a/clang/lib/AST/QualTypeNames.cpp +++ b/clang/lib/AST/QualTypeNames.cpp @@ -129,11 +129,9 @@ static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx, if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) { bool MightHaveChanged = false; SmallVector<TemplateArgument, 4> FQArgs; - for (TemplateSpecializationType::iterator I = TST->begin(), E = TST->end(); - I != E; ++I) { - // Cheap to copy and potentially modified by - // getFullyQualifedTemplateArgument. - TemplateArgument Arg(*I); + // Cheap to copy and potentially modified by + // getFullyQualifedTemplateArgument. + for (TemplateArgument Arg : TST->template_arguments()) { MightHaveChanged |= getFullyQualifiedTemplateArgument( Ctx, Arg, WithGlobalNsPrefix); FQArgs.push_back(Arg); @@ -422,13 +420,6 @@ QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, return QT; } - // We don't consider the alias introduced by `using a::X` as a new type. - // The qualified name is still a::X. - if (isa<UsingType>(QT.getTypePtr())) { - return getFullyQualifiedType(QT.getSingleStepDesugaredType(Ctx), Ctx, - WithGlobalNsPrefix); - } - // Remove the part of the type related to the type being a template // parameter (we won't report it as part of the 'type name' and it // is actually make the code below to be more complex (to handle @@ -455,6 +446,14 @@ QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, assert(!QT.hasLocalQualifiers()); Keyword = ETypeInput->getKeyword(); } + + // We don't consider the alias introduced by `using a::X` as a new type. + // The qualified name is still a::X. + if (const auto *UT = QT->getAs<UsingType>()) { + QT = Ctx.getQualifiedType(UT->getUnderlyingType(), PrefixQualifiers); + return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix); + } + // Create a nested name specifier if needed. Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(), true /*FullyQualified*/, diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 6f3ede2ce42a..2f546398338c 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -1059,10 +1059,10 @@ void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases( // primary base, add it in now. } else if (RD->isDynamicClass()) { assert(DataSize == 0 && "Vtable pointer must be at offset zero!"); - CharUnits PtrWidth = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - CharUnits PtrAlign = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); + CharUnits PtrWidth = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerWidth(LangAS::Default)); + CharUnits PtrAlign = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerAlign(LangAS::Default)); EnsureVTablePointerAlignment(PtrAlign); HasOwnVFPtr = true; @@ -1890,11 +1890,6 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, LastBitfieldStorageUnitSize = 0; llvm::Triple Target = Context.getTargetInfo().getTriple(); - bool FieldPacked = (Packed && (!FieldClass || FieldClass->isPOD() || - Context.getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver14 || - Target.isPS() || Target.isOSDarwin())) || - D->hasAttr<PackedAttr>(); AlignRequirementKind AlignRequirement = AlignRequirementKind::None; CharUnits FieldSize; @@ -1916,12 +1911,6 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, if (D->getType()->isIncompleteArrayType()) { setDeclInfo(true /* IsIncompleteArrayType */); - } else if (const ReferenceType *RT = D->getType()->getAs<ReferenceType>()) { - unsigned AS = Context.getTargetAddressSpace(RT->getPointeeType()); - EffectiveFieldSize = FieldSize = Context.toCharUnitsFromBits( - Context.getTargetInfo().getPointerWidth(AS)); - FieldAlign = Context.toCharUnitsFromBits( - Context.getTargetInfo().getPointerAlign(AS)); } else { setDeclInfo(false /* IsIncompleteArrayType */); @@ -1975,6 +1964,14 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, } } + bool FieldPacked = (Packed && (!FieldClass || FieldClass->isPOD() || + FieldClass->hasAttr<PackedAttr>() || + Context.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver15 || + Target.isPS() || Target.isOSDarwin() || + Target.isOSAIX())) || + D->hasAttr<PackedAttr>(); + // When used as part of a typedef, or together with a 'packed' attribute, the // 'aligned' attribute can be used to decrease alignment. In that case, it // overrides any computed alignment we have, and there is no need to upgrade @@ -2025,28 +2022,34 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, // The align if the field is not packed. This is to check if the attribute // was unnecessary (-Wpacked). - CharUnits UnpackedFieldAlign = - !DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign; + CharUnits UnpackedFieldAlign = FieldAlign; + CharUnits PackedFieldAlign = CharUnits::One(); CharUnits UnpackedFieldOffset = FieldOffset; CharUnits OriginalFieldAlign = UnpackedFieldAlign; - if (FieldPacked) { - FieldAlign = CharUnits::One(); - PreferredAlign = CharUnits::One(); - } CharUnits MaxAlignmentInChars = Context.toCharUnitsFromBits(D->getMaxAlignment()); - FieldAlign = std::max(FieldAlign, MaxAlignmentInChars); + PackedFieldAlign = std::max(PackedFieldAlign, MaxAlignmentInChars); PreferredAlign = std::max(PreferredAlign, MaxAlignmentInChars); UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars); // The maximum field alignment overrides the aligned attribute. if (!MaxFieldAlignment.isZero()) { - FieldAlign = std::min(FieldAlign, MaxFieldAlignment); + PackedFieldAlign = std::min(PackedFieldAlign, MaxFieldAlignment); PreferredAlign = std::min(PreferredAlign, MaxFieldAlignment); UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment); } + + if (!FieldPacked) + FieldAlign = UnpackedFieldAlign; + if (DefaultsToAIXPowerAlignment) + UnpackedFieldAlign = PreferredAlign; + if (FieldPacked) { + PreferredAlign = PackedFieldAlign; + FieldAlign = PackedFieldAlign; + } + CharUnits AlignTo = !DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign; // Round up the current record size to the field's alignment boundary. @@ -2129,6 +2132,9 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, << Context.getTypeDeclType(RD) << D->getName() << D->getType(); } } + + if (Packed && !FieldPacked && PackedFieldAlign < FieldAlign) + Diag(D->getLocation(), diag::warn_unpacked_field) << D; } void ItaniumRecordLayoutBuilder::FinishLayout(const NamedDecl *D) { @@ -2757,7 +2763,8 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) { // than the pointer size. if (const MaxFieldAlignmentAttr *MFAA = RD->getAttr<MaxFieldAlignmentAttr>()){ unsigned PackedAlignment = MFAA->getAlignment(); - if (PackedAlignment <= Context.getTargetInfo().getPointerWidth(0)) + if (PackedAlignment <= + Context.getTargetInfo().getPointerWidth(LangAS::Default)) MaxFieldAlignment = Context.toCharUnitsFromBits(PackedAlignment); } // Packed attribute forces max field alignment to be 1. @@ -2782,10 +2789,10 @@ MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) { SharedVBPtrBase = nullptr; // Calculate pointer size and alignment. These are used for vfptr and vbprt // injection. - PointerInfo.Size = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - PointerInfo.Alignment = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); + PointerInfo.Size = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerWidth(LangAS::Default)); + PointerInfo.Alignment = Context.toCharUnitsFromBits( + Context.getTargetInfo().getPointerAlign(LangAS::Default)); // Respect pragma pack. if (!MaxFieldAlignment.isZero()) PointerInfo.Alignment = std::min(PointerInfo.Alignment, MaxFieldAlignment); @@ -3070,10 +3077,9 @@ void MicrosoftRecordLayoutBuilder::injectVFPtr(const CXXRecordDecl *RD) { VBPtrOffset += Offset; if (UseExternalLayout) { - // The class may have no bases or fields, but still have a vfptr - // (e.g. it's an interface class). The size was not correctly set before - // in this case. - if (FieldOffsets.empty() && Bases.empty()) + // The class may have size 0 and a vfptr (e.g. it's an interface class). The + // size was not correctly set before in this case. + if (Size.isZero()) Size += Offset; return; } @@ -3275,6 +3281,8 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { if (D->hasExternalLexicalStorage() && !D->getDefinition()) getExternalSource()->CompleteType(const_cast<RecordDecl*>(D)); + // Complete the redecl chain (if necessary). + (void)D->getMostRecentDecl(); D = D->getDefinition(); assert(D && "Cannot get layout of forward declarations!"); diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 8d763f28e57f..d6ff1a616285 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -161,6 +161,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, default: break; case '%': k = ConversionSpecifier::PercentArg; break; + case 'b': k = ConversionSpecifier::bArg; break; case 'A': k = ConversionSpecifier::AArg; break; case 'E': k = ConversionSpecifier::EArg; break; case 'F': k = ConversionSpecifier::FArg; break; @@ -267,6 +268,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { llvm_unreachable("Unsupported LengthModifier Type"); // Unsigned int. + case ConversionSpecifier::bArg: case ConversionSpecifier::oArg: case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: @@ -343,7 +345,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsShort: if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) return ArgType::PtrTo(ArgType::AnyCharTy); - LLVM_FALLTHROUGH; + [[fallthrough]]; default: return ArgType::Invalid(); } @@ -360,7 +362,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsShort: if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) return ArgType::PtrTo(ArgType::AnyCharTy); - LLVM_FALLTHROUGH; + [[fallthrough]]; default: return ArgType::Invalid(); } @@ -500,7 +502,7 @@ bool ScanfSpecifier::fixType(QualType QT, QualType RawQT, } // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) + if (LangOpt.C99 || LangOpt.CPlusPlus11) namedTypeToLengthModifier(PT, LM); // If fixing the length modifier was enough, we are done. diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 8eae04d0d9fd..8744bba6c6d9 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -41,6 +41,7 @@ #include <algorithm> #include <cassert> #include <cstring> +#include <optional> #include <string> #include <type_traits> #include <utility> @@ -1001,18 +1002,18 @@ bool IfStmt::isObjCAvailabilityCheck() const { return isa<ObjCAvailabilityCheckExpr>(getCond()); } -Optional<Stmt *> IfStmt::getNondiscardedCase(const ASTContext &Ctx) { +std::optional<Stmt *> IfStmt::getNondiscardedCase(const ASTContext &Ctx) { if (!isConstexpr() || getCond()->isValueDependent()) - return None; + return std::nullopt; return !getCond()->EvaluateKnownConstInt(Ctx) ? getElse() : getThen(); } -Optional<const Stmt *> +std::optional<const Stmt *> IfStmt::getNondiscardedCase(const ASTContext &Ctx) const { - if (Optional<Stmt *> Result = + if (std::optional<Stmt *> Result = const_cast<IfStmt *>(this)->getNondiscardedCase(Ctx)) return *Result; - return None; + return std::nullopt; } ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index e0a4221db7ec..7c5b9f23fc26 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -31,7 +31,7 @@ void OMPChildren::setClauses(ArrayRef<OMPClause *> Clauses) { } MutableArrayRef<Stmt *> OMPChildren::getChildren() { - return llvm::makeMutableArrayRef(getTrailingObjects<Stmt *>(), NumChildren); + return llvm::MutableArrayRef(getTrailingObjects<Stmt *>(), NumChildren); } OMPChildren *OMPChildren::Create(void *Mem, ArrayRef<OMPClause *> Clauses) { @@ -517,7 +517,7 @@ OMPSectionDirective *OMPSectionDirective::Create(const ASTContext &C, Stmt *AssociatedStmt, bool HasCancel) { auto *Dir = - createDirective<OMPSectionDirective>(C, llvm::None, AssociatedStmt, + createDirective<OMPSectionDirective>(C, std::nullopt, AssociatedStmt, /*NumChildren=*/0, StartLoc, EndLoc); Dir->setHasCancel(HasCancel); return Dir; @@ -550,7 +550,7 @@ OMPMasterDirective *OMPMasterDirective::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, Stmt *AssociatedStmt) { - return createDirective<OMPMasterDirective>(C, llvm::None, AssociatedStmt, + return createDirective<OMPMasterDirective>(C, std::nullopt, AssociatedStmt, /*NumChildren=*/0, StartLoc, EndLoc); } @@ -744,6 +744,21 @@ OMPTaskyieldDirective *OMPTaskyieldDirective::CreateEmpty(const ASTContext &C, return new (C) OMPTaskyieldDirective(); } +OMPErrorDirective *OMPErrorDirective::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses) { + return createDirective<OMPErrorDirective>( + C, Clauses, /*AssociatedStmt=*/nullptr, /*NumChildren=*/0, StartLoc, + EndLoc); +} + +OMPErrorDirective *OMPErrorDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + EmptyShell) { + return createEmptyDirective<OMPErrorDirective>(C, NumClauses); +} + OMPBarrierDirective *OMPBarrierDirective::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc) { diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 8d778500d103..0a879bb6df2a 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -54,6 +54,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include <cassert> +#include <optional> #include <string> using namespace clang; @@ -843,6 +844,11 @@ void StmtPrinter::VisitOMPTaskwaitDirective(OMPTaskwaitDirective *Node) { PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPErrorDirective(OMPErrorDirective *Node) { + Indent() << "#pragma omp error"; + PrintOMPExecutableDirective(Node); +} + void StmtPrinter::VisitOMPTaskgroupDirective(OMPTaskgroupDirective *Node) { Indent() << "#pragma omp taskgroup"; PrintOMPExecutableDirective(Node); @@ -1280,6 +1286,7 @@ void StmtPrinter::VisitIntegerLiteral(IntegerLiteral *Node) { case BuiltinType::Char_S: case BuiltinType::Char_U: OS << "i8"; break; case BuiltinType::UChar: OS << "Ui8"; break; + case BuiltinType::SChar: OS << "i8"; break; case BuiltinType::Short: OS << "i16"; break; case BuiltinType::UShort: OS << "Ui16"; break; case BuiltinType::Int: break; // no suffix. @@ -1292,6 +1299,9 @@ void StmtPrinter::VisitIntegerLiteral(IntegerLiteral *Node) { break; // no suffix. case BuiltinType::UInt128: break; // no suffix. + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + break; // no suffix } } @@ -1993,7 +2003,7 @@ void StmtPrinter::VisitUserDefinedLiteral(UserDefinedLiteral *Node) { cast<FunctionDecl>(DRE->getDecl())->getTemplateSpecializationArgs(); assert(Args); - if (Args->size() != 1) { + if (Args->size() != 1 || Args->get(0).getKind() != TemplateArgument::Pack) { const TemplateParameterList *TPL = nullptr; if (!DRE->hadMultipleCandidates()) if (const auto *TD = dyn_cast<TemplateDecl>(DRE->getDecl())) @@ -2164,7 +2174,8 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) { OS << "..."; if (Node->isInitCapture(C)) { - VarDecl *D = C->getCapturedVar(); + // Init captures are always VarDecl. + auto *D = cast<VarDecl>(C->getCapturedVar()); llvm::StringRef Pre; llvm::StringRef Post; @@ -2265,7 +2276,7 @@ void StmtPrinter::VisitCXXNewExpr(CXXNewExpr *E) { if (E->isArray()) { llvm::raw_string_ostream s(TypeS); s << '['; - if (Optional<Expr *> Size = E->getArraySize()) + if (std::optional<Expr *> Size = E->getArraySize()) (*Size)->printPretty(s, Helper, Policy); s << ']'; } @@ -2455,6 +2466,13 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) { OS << ")"; } +void StmtPrinter::VisitCXXParenListInitExpr(CXXParenListInitExpr *Node) { + OS << "("; + llvm::interleaveComma(Node->getInitExprs(), OS, + [&](Expr *E) { PrintExpr(E); }); + OS << ")"; +} + void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc(); if (NNS) @@ -2511,7 +2529,7 @@ void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { } else { auto *NestedReq = cast<concepts::NestedRequirement>(Req); OS << "requires "; - if (NestedReq->isSubstitutionFailure()) + if (NestedReq->hasInvalidConstraint()) OS << "<<error-expression>>"; else PrintExpr(NestedReq->getConstraintExpr()); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 92a8b18cf68a..960cc4f4fc27 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -472,7 +472,7 @@ void OMPClauseProfiler::VisitOMPSimdlenClause(const OMPSimdlenClause *C) { } void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) { - for (auto E : C->getSizesRefs()) + for (auto *E : C->getSizesRefs()) if (E) Profiler->VisitExpr(E); } @@ -530,6 +530,15 @@ void OMPClauseProfiler::VisitOMPDynamicAllocatorsClause( void OMPClauseProfiler::VisitOMPAtomicDefaultMemOrderClause( const OMPAtomicDefaultMemOrderClause *C) {} +void OMPClauseProfiler::VisitOMPAtClause(const OMPAtClause *C) {} + +void OMPClauseProfiler::VisitOMPSeverityClause(const OMPSeverityClause *C) {} + +void OMPClauseProfiler::VisitOMPMessageClause(const OMPMessageClause *C) { + if (C->getMessageString()) + Profiler->VisitStmt(C->getMessageString()); +} + void OMPClauseProfiler::VisitOMPScheduleClause(const OMPScheduleClause *C) { VistOMPClauseWithPreInit(C); if (auto *S = C->getChunkSize()) @@ -894,6 +903,12 @@ void OMPClauseProfiler::VisitOMPAffinityClause(const OMPAffinityClause *C) { } void OMPClauseProfiler::VisitOMPOrderClause(const OMPOrderClause *C) {} void OMPClauseProfiler::VisitOMPBindClause(const OMPBindClause *C) {} +void OMPClauseProfiler::VisitOMPXDynCGroupMemClause( + const OMPXDynCGroupMemClause *C) { + VistOMPClauseWithPreInit(C); + if (Expr *Size = C->getSize()) + Profiler->VisitStmt(Size); +} } // namespace void @@ -1014,6 +1029,9 @@ void StmtProfiler::VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *S) { VisitOMPExecutableDirective(S); } +void StmtProfiler::VisitOMPErrorDirective(const OMPErrorDirective *S) { + VisitOMPExecutableDirective(S); +} void StmtProfiler::VisitOMPTaskgroupDirective(const OMPTaskgroupDirective *S) { VisitOMPExecutableDirective(S); if (const Expr *E = S->getReductionRef()) @@ -1610,8 +1628,8 @@ void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) { } else { ID.AddInteger(concepts::Requirement::RK_Nested); auto *NestedReq = cast<concepts::NestedRequirement>(Req); - ID.AddBoolean(NestedReq->isSubstitutionFailure()); - if (!NestedReq->isSubstitutionFailure()) + ID.AddBoolean(NestedReq->hasInvalidConstraint()); + if (!NestedReq->hasInvalidConstraint()) Visit(NestedReq->getConstraintExpr()); } } @@ -2181,6 +2199,10 @@ void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) { ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXParenListInitExpr(const CXXParenListInitExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { VisitStmt(S); } diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index e0f5916a9a0b..ceff7a313716 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -29,7 +29,6 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -41,6 +40,7 @@ #include <cstddef> #include <cstdint> #include <cstring> +#include <optional> using namespace clang; @@ -271,12 +271,12 @@ bool TemplateArgument::containsUnexpandedParameterPack() const { return getDependence() & TemplateArgumentDependence::UnexpandedPack; } -Optional<unsigned> TemplateArgument::getNumTemplateExpansions() const { +std::optional<unsigned> TemplateArgument::getNumTemplateExpansions() const { assert(getKind() == TemplateExpansion); if (TemplateArg.NumExpansions) return TemplateArg.NumExpansions - 1; - return None; + return std::nullopt; } QualType TemplateArgument::getNonTypeTemplateArgumentType() const { @@ -321,26 +321,15 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, case Declaration: getParamTypeForDecl().Profile(ID); - ID.AddPointer(getAsDecl()? getAsDecl()->getCanonicalDecl() : nullptr); + ID.AddPointer(getAsDecl()); break; + case TemplateExpansion: + ID.AddInteger(TemplateArg.NumExpansions); + LLVM_FALLTHROUGH; case Template: - case TemplateExpansion: { - TemplateName Template = getAsTemplateOrTemplatePattern(); - if (TemplateTemplateParmDecl *TTP - = dyn_cast_or_null<TemplateTemplateParmDecl>( - Template.getAsTemplateDecl())) { - ID.AddBoolean(true); - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getPosition()); - ID.AddBoolean(TTP->isParameterPack()); - } else { - ID.AddBoolean(false); - ID.AddPointer(Context.getCanonicalTemplateName(Template) - .getAsVoidPointer()); - } + getAsTemplateOrTemplatePattern().Profile(ID); break; - } case Integral: getAsIntegral().Profile(ID); @@ -374,7 +363,8 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { TemplateArg.NumExpansions == Other.TemplateArg.NumExpansions; case Declaration: - return getAsDecl() == Other.getAsDecl(); + return getAsDecl() == Other.getAsDecl() && + getParamTypeForDecl() == Other.getParamTypeForDecl(); case Integral: return getIntegralType() == Other.getIntegralType() && @@ -432,10 +422,10 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, } case Declaration: { - // FIXME: Include the type if it's not obvious from the context. NamedDecl *ND = getAsDecl(); if (getParamTypeForDecl()->isRecordType()) { if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) { + TPO->getType().getUnqualifiedType().print(Out, Policy); TPO->printAsInit(Out, Policy); break; } diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index 11dc3d2e1985..a6dd0fad9331 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -29,37 +29,74 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include <cassert> +#include <optional> #include <string> using namespace clang; TemplateArgument SubstTemplateTemplateParmPackStorage::getArgumentPack() const { - return TemplateArgument(llvm::makeArrayRef(Arguments, size())); + return TemplateArgument(llvm::ArrayRef(Arguments, Bits.Data)); +} + +TemplateTemplateParmDecl * +SubstTemplateTemplateParmPackStorage::getParameterPack() const { + return cast<TemplateTemplateParmDecl>( + getReplacedTemplateParameterList(getAssociatedDecl()) + ->asArray()[Bits.Index]); +} + +TemplateTemplateParmDecl * +SubstTemplateTemplateParmStorage::getParameter() const { + return cast<TemplateTemplateParmDecl>( + getReplacedTemplateParameterList(getAssociatedDecl()) + ->asArray()[Bits.Index]); } void SubstTemplateTemplateParmStorage::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, Parameter, Replacement); + Profile(ID, Replacement, getAssociatedDecl(), getIndex(), getPackIndex()); +} + +void SubstTemplateTemplateParmStorage::Profile( + llvm::FoldingSetNodeID &ID, TemplateName Replacement, Decl *AssociatedDecl, + unsigned Index, std::optional<unsigned> PackIndex) { + Replacement.Profile(ID); + ID.AddPointer(AssociatedDecl); + ID.AddInteger(Index); + ID.AddInteger(PackIndex ? *PackIndex + 1 : 0); } -void SubstTemplateTemplateParmStorage::Profile(llvm::FoldingSetNodeID &ID, - TemplateTemplateParmDecl *parameter, - TemplateName replacement) { - ID.AddPointer(parameter); - ID.AddPointer(replacement.getAsVoidPointer()); +SubstTemplateTemplateParmPackStorage::SubstTemplateTemplateParmPackStorage( + ArrayRef<TemplateArgument> ArgPack, Decl *AssociatedDecl, unsigned Index, + bool Final) + : UncommonTemplateNameStorage(SubstTemplateTemplateParmPack, Index, + ArgPack.size()), + Arguments(ArgPack.data()), AssociatedDeclAndFinal(AssociatedDecl, Final) { + assert(AssociatedDecl != nullptr); } void SubstTemplateTemplateParmPackStorage::Profile(llvm::FoldingSetNodeID &ID, ASTContext &Context) { - Profile(ID, Context, Parameter, getArgumentPack()); + Profile(ID, Context, getArgumentPack(), getAssociatedDecl(), getIndex(), + getFinal()); } -void SubstTemplateTemplateParmPackStorage::Profile(llvm::FoldingSetNodeID &ID, - ASTContext &Context, - TemplateTemplateParmDecl *Parameter, - const TemplateArgument &ArgPack) { - ID.AddPointer(Parameter); +Decl *SubstTemplateTemplateParmPackStorage::getAssociatedDecl() const { + return AssociatedDeclAndFinal.getPointer(); +} + +bool SubstTemplateTemplateParmPackStorage::getFinal() const { + return AssociatedDeclAndFinal.getInt(); +} + +void SubstTemplateTemplateParmPackStorage::Profile( + llvm::FoldingSetNodeID &ID, ASTContext &Context, + const TemplateArgument &ArgPack, Decl *AssociatedDecl, unsigned Index, + bool Final) { ArgPack.Profile(ID, Context); + ID.AddPointer(AssociatedDecl); + ID.AddInteger(Index); + ID.AddBoolean(Final); } TemplateName::TemplateName(void *Ptr) { @@ -304,7 +341,7 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, } else { assert(getKind() == TemplateName::OverloadedTemplate); OverloadedTemplateStorage *OTS = getAsOverloadedTemplate(); - (*OTS->begin())->printName(OS); + (*OTS->begin())->printName(OS, Policy); } } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 22643d4edbec..a5573c117e62 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1543,17 +1543,23 @@ void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) { void TextNodeDumper::VisitUsingType(const UsingType *T) { dumpDeclRef(T->getFoundDecl()); + if (!T->typeMatchesDecl()) + OS << " divergent"; } void TextNodeDumper::VisitTypedefType(const TypedefType *T) { dumpDeclRef(T->getDecl()); + if (!T->typeMatchesDecl()) + OS << " divergent"; } void TextNodeDumper::VisitUnaryTransformType(const UnaryTransformType *T) { switch (T->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - OS << " underlying_type"; +#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \ + case UnaryTransformType::Enum: \ + OS << " " #Trait; \ break; +#include "clang/Basic/TransformTypeTraits.def" } } @@ -1568,6 +1574,20 @@ void TextNodeDumper::VisitTemplateTypeParmType(const TemplateTypeParmType *T) { dumpDeclRef(T->getDecl()); } +void TextNodeDumper::VisitSubstTemplateTypeParmType( + const SubstTemplateTypeParmType *T) { + dumpDeclRef(T->getAssociatedDecl()); + VisitTemplateTypeParmDecl(T->getReplacedParameter()); + if (auto PackIndex = T->getPackIndex()) + OS << " pack_index " << *PackIndex; +} + +void TextNodeDumper::VisitSubstTemplateTypeParmPackType( + const SubstTemplateTypeParmPackType *T) { + dumpDeclRef(T->getAssociatedDecl()); + VisitTemplateTypeParmDecl(T->getReplacedParameter()); +} + void TextNodeDumper::VisitAutoType(const AutoType *T) { if (T->isDecltypeAuto()) OS << " decltype(auto)"; @@ -1786,6 +1806,8 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { case VarDecl::ListInit: OS << " listinit"; break; + case VarDecl::ParenListInit: + OS << " parenlistinit"; } } if (D->needsDestruction(D->getASTContext())) @@ -1911,6 +1933,8 @@ void TextNodeDumper::VisitNamespaceDecl(const NamespaceDecl *D) { dumpName(D); if (D->isInline()) OS << " inline"; + if (D->isNested()) + OS << " nested"; if (!D->isOriginalNamespace()) dumpDeclRef(D->getOriginalNamespace(), "original"); } @@ -2380,3 +2404,11 @@ void TextNodeDumper::VisitCompoundStmt(const CompoundStmt *S) { if (S->hasStoredFPFeatures()) printFPOptions(S->getStoredFPFeatures()); } + +void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { + if (D->isCBuffer()) + OS << " cbuffer"; + else + OS << " tbuffer"; + dumpName(D); +} diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 0f168a518707..a713d6e3bd03 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -42,7 +42,6 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -51,6 +50,7 @@ #include <cassert> #include <cstdint> #include <cstring> +#include <optional> #include <type_traits> using namespace clang; @@ -525,6 +525,10 @@ template <> const TypedefType *Type::getAs() const { return getAsSugar<TypedefType>(this); } +template <> const UsingType *Type::getAs() const { + return getAsSugar<UsingType>(this); +} + template <> const TemplateSpecializationType *Type::getAs() const { return getAsSugar<TemplateSpecializationType>(this); } @@ -1073,7 +1077,7 @@ public: if (exceptionChanged) { info.ExceptionSpec.Exceptions = - llvm::makeArrayRef(exceptionTypes).copy(Ctx); + llvm::ArrayRef(exceptionTypes).copy(Ctx); } } @@ -1166,8 +1170,9 @@ public: == T->getReplacementType().getAsOpaquePtr()) return QualType(T, 0); - return Ctx.getSubstTemplateTypeParmType(T->getReplacedParameter(), - replacementType); + return Ctx.getSubstTemplateTypeParmType(replacementType, + T->getAssociatedDecl(), + T->getIndex(), T->getPackIndex()); } // FIXME: Non-trivial to implement, but important for C++ @@ -1214,10 +1219,10 @@ public: !typeArgChanged) return QualType(T, 0); - return Ctx.getObjCObjectType(baseType, typeArgs, - llvm::makeArrayRef(T->qual_begin(), - T->getNumProtocols()), - T->isKindOfTypeAsWritten()); + return Ctx.getObjCObjectType( + baseType, typeArgs, + llvm::ArrayRef(T->qual_begin(), T->getNumProtocols()), + T->isKindOfTypeAsWritten()); } TRIVIAL_TYPE_CLASS(ObjCInterface) @@ -1369,7 +1374,7 @@ struct SubstObjCTypeArgsVisitor if (exceptionChanged) { info.ExceptionSpec.Exceptions = - llvm::makeArrayRef(exceptionTypes).copy(Ctx); + llvm::ArrayRef(exceptionTypes).copy(Ctx); } } @@ -1479,6 +1484,25 @@ struct StripObjCKindOfTypeVisitor } // namespace +bool QualType::UseExcessPrecision(const ASTContext &Ctx) { + const BuiltinType *BT = getTypePtr()->getAs<BuiltinType>(); + if (BT) { + switch (BT->getKind()) { + case BuiltinType::Kind::Float16: { + const TargetInfo &TI = Ctx.getTargetInfo(); + if (TI.hasFloat16Type() && !TI.hasLegalHalfType() && + Ctx.getLangOpts().getFloat16ExcessPrecision() != + Ctx.getLangOpts().ExcessPrecisionKind::FPP_None) + return true; + return false; + } + default: + return false; + } + } + return false; +} + /// Substitute the given type arguments for Objective-C type /// parameters within the given type, recursively. QualType QualType::substObjCTypeArgs(ASTContext &ctx, @@ -1510,8 +1534,8 @@ QualType QualType::getAtomicUnqualifiedType() const { return getUnqualifiedType(); } -Optional<ArrayRef<QualType>> Type::getObjCSubstitutions( - const DeclContext *dc) const { +std::optional<ArrayRef<QualType>> +Type::getObjCSubstitutions(const DeclContext *dc) const { // Look through method scopes. if (const auto method = dyn_cast<ObjCMethodDecl>(dc)) dc = method->getDeclContext(); @@ -1526,23 +1550,23 @@ Optional<ArrayRef<QualType>> Type::getObjCSubstitutions( // substitution to do. dcTypeParams = dcClassDecl->getTypeParamList(); if (!dcTypeParams) - return None; + return std::nullopt; } else { // If we are in neither a class nor a category, there's no // substitution to perform. dcCategoryDecl = dyn_cast<ObjCCategoryDecl>(dc); if (!dcCategoryDecl) - return None; + return std::nullopt; // If the category does not have any type parameters, there's no // substitution to do. dcTypeParams = dcCategoryDecl->getTypeParamList(); if (!dcTypeParams) - return None; + return std::nullopt; dcClassDecl = dcCategoryDecl->getClassInterface(); if (!dcClassDecl) - return None; + return std::nullopt; } assert(dcTypeParams && "No substitutions to perform"); assert(dcClassDecl && "No class context"); @@ -2320,6 +2344,20 @@ bool Type::isSizelessBuiltinType() const { bool Type::isSizelessType() const { return isSizelessBuiltinType(); } +bool Type::isSVESizelessBuiltinType() const { + if (const BuiltinType *BT = getAs<BuiltinType>()) { + switch (BT->getKind()) { + // SVE Types +#define SVE_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" + return true; + default: + return false; + } + } + return false; +} + bool Type::isVLSTBuiltinType() const { if (const BuiltinType *BT = getAs<BuiltinType>()) { switch (BT->getKind()) { @@ -2777,39 +2815,6 @@ bool Type::isStdByteType() const { return false; } -bool Type::isPromotableIntegerType() const { - if (const auto *BT = getAs<BuiltinType>()) - switch (BT->getKind()) { - case BuiltinType::Bool: - case BuiltinType::Char_S: - case BuiltinType::Char_U: - case BuiltinType::SChar: - case BuiltinType::UChar: - case BuiltinType::Short: - case BuiltinType::UShort: - case BuiltinType::WChar_S: - case BuiltinType::WChar_U: - case BuiltinType::Char8: - case BuiltinType::Char16: - case BuiltinType::Char32: - return true; - default: - return false; - } - - // Enumerated types are promotable to their compatible integer types - // (C99 6.3.1.1) a.k.a. its underlying type (C++ [conv.prom]p2). - if (const auto *ET = getAs<EnumType>()){ - if (this->isDependentType() || ET->getDecl()->getPromotionType().isNull() - || ET->getDecl()->isScoped()) - return false; - - return true; - } - - return false; -} - bool Type::isSpecifierType() const { // Note that this intentionally does not use the canonical type. switch (getTypeClass()) { @@ -2928,7 +2933,7 @@ DependentTemplateSpecializationType::DependentTemplateSpecializationType( DependentTemplateSpecializationTypeBits.NumArgs = Args.size(); assert((!NNS || NNS->isDependent()) && "DependentTemplateSpecializatonType requires dependent qualifier"); - TemplateArgument *ArgBuffer = getArgBuffer(); + auto *ArgBuffer = const_cast<TemplateArgument *>(template_arguments().data()); for (const TemplateArgument &Arg : Args) { addDependence(toTypeDependence(Arg.getDependence() & TemplateArgumentDependence::UnexpandedPack)); @@ -3084,7 +3089,7 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { case Char32: return "char32_t"; case NullPtr: - return "std::nullptr_t"; + return Policy.NullptrTypeInNamespace ? "std::nullptr_t" : "nullptr_t"; case Overload: return "<overloaded function type>"; case BoundMember: @@ -3434,25 +3439,34 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, } TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, - QualType underlying, QualType can) - : Type(tc, can, toSemanticDependence(underlying->getDependence())), + QualType Underlying, QualType can) + : Type(tc, can, toSemanticDependence(can->getDependence())), Decl(const_cast<TypedefNameDecl *>(D)) { assert(!isa<TypedefType>(can) && "Invalid canonical type"); + TypedefBits.hasTypeDifferentFromDecl = !Underlying.isNull(); + if (!typeMatchesDecl()) + *getTrailingObjects<QualType>() = Underlying; } QualType TypedefType::desugar() const { - return getDecl()->getUnderlyingType(); + return typeMatchesDecl() ? Decl->getUnderlyingType() + : *getTrailingObjects<QualType>(); } UsingType::UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon) - : Type(Using, Canon, toSemanticDependence(Underlying->getDependence())), + : Type(Using, Canon, toSemanticDependence(Canon->getDependence())), Found(const_cast<UsingShadowDecl *>(Found)) { - assert(Underlying == getUnderlyingType()); + UsingBits.hasTypeDifferentFromDecl = !Underlying.isNull(); + if (!typeMatchesDecl()) + *getTrailingObjects<QualType>() = Underlying; } QualType UsingType::getUnderlyingType() const { - return QualType(cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl(), 0); + return typeMatchesDecl() + ? QualType( + cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl(), 0) + : *getTrailingObjects<QualType>(); } QualType MacroQualifiedType::desugar() const { return getUnderlyingType(); } @@ -3469,27 +3483,37 @@ QualType MacroQualifiedType::getModifiedType() const { return Inner; } -TypeOfExprType::TypeOfExprType(Expr *E, QualType can) - : Type(TypeOfExpr, can, +TypeOfExprType::TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can) + : Type(TypeOfExpr, + // We have to protect against 'Can' being invalid through its + // default argument. + Kind == TypeOfKind::Unqualified && !Can.isNull() + ? Can.getAtomicUnqualifiedType() + : Can, toTypeDependence(E->getDependence()) | (E->getType()->getDependence() & TypeDependence::VariablyModified)), - TOExpr(E) {} + TOExpr(E) { + TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified; +} bool TypeOfExprType::isSugared() const { return !TOExpr->isTypeDependent(); } QualType TypeOfExprType::desugar() const { - if (isSugared()) - return getUnderlyingExpr()->getType(); - + if (isSugared()) { + QualType QT = getUnderlyingExpr()->getType(); + return TypeOfBits.IsUnqual ? QT.getAtomicUnqualifiedType() : QT; + } return QualType(this, 0); } void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID, - const ASTContext &Context, Expr *E) { + const ASTContext &Context, Expr *E, + bool IsUnqual) { E->Profile(ID, Context, true); + ID.AddBoolean(IsUnqual); } DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can) @@ -3539,7 +3563,7 @@ TagType::TagType(TypeClass TC, const TagDecl *D, QualType can) decl(const_cast<TagDecl *>(D)) {} static TagDecl *getInterestingTagDecl(TagDecl *decl) { - for (auto I : decl->redecls()) { + for (auto *I : decl->redecls()) { if (I->isCompleteDefinition() || I->isBeingDefined()) return I; } @@ -3649,28 +3673,80 @@ IdentifierInfo *TemplateTypeParmType::getIdentifier() const { return isCanonicalUnqualified() ? nullptr : getDecl()->getIdentifier(); } +static const TemplateTypeParmDecl *getReplacedParameter(Decl *D, + unsigned Index) { + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) + return TTP; + return cast<TemplateTypeParmDecl>( + getReplacedTemplateParameterList(D)->getParam(Index)); +} + +SubstTemplateTypeParmType::SubstTemplateTypeParmType( + QualType Replacement, Decl *AssociatedDecl, unsigned Index, + std::optional<unsigned> PackIndex) + : Type(SubstTemplateTypeParm, Replacement.getCanonicalType(), + Replacement->getDependence()), + AssociatedDecl(AssociatedDecl) { + SubstTemplateTypeParmTypeBits.HasNonCanonicalUnderlyingType = + Replacement != getCanonicalTypeInternal(); + if (SubstTemplateTypeParmTypeBits.HasNonCanonicalUnderlyingType) + *getTrailingObjects<QualType>() = Replacement; + + SubstTemplateTypeParmTypeBits.Index = Index; + SubstTemplateTypeParmTypeBits.PackIndex = PackIndex ? *PackIndex + 1 : 0; + assert(AssociatedDecl != nullptr); +} + +const TemplateTypeParmDecl * +SubstTemplateTypeParmType::getReplacedParameter() const { + return ::getReplacedParameter(getAssociatedDecl(), getIndex()); +} + SubstTemplateTypeParmPackType::SubstTemplateTypeParmPackType( - const TemplateTypeParmType *Param, QualType Canon, + QualType Canon, Decl *AssociatedDecl, unsigned Index, bool Final, const TemplateArgument &ArgPack) : Type(SubstTemplateTypeParmPack, Canon, TypeDependence::DependentInstantiation | TypeDependence::UnexpandedPack), - Replaced(Param), Arguments(ArgPack.pack_begin()) { + Arguments(ArgPack.pack_begin()), + AssociatedDeclAndFinal(AssociatedDecl, Final) { + SubstTemplateTypeParmPackTypeBits.Index = Index; SubstTemplateTypeParmPackTypeBits.NumArgs = ArgPack.pack_size(); + assert(AssociatedDecl != nullptr); +} + +Decl *SubstTemplateTypeParmPackType::getAssociatedDecl() const { + return AssociatedDeclAndFinal.getPointer(); +} + +bool SubstTemplateTypeParmPackType::getFinal() const { + return AssociatedDeclAndFinal.getInt(); +} + +const TemplateTypeParmDecl * +SubstTemplateTypeParmPackType::getReplacedParameter() const { + return ::getReplacedParameter(getAssociatedDecl(), getIndex()); +} + +IdentifierInfo *SubstTemplateTypeParmPackType::getIdentifier() const { + return getReplacedParameter()->getIdentifier(); } TemplateArgument SubstTemplateTypeParmPackType::getArgumentPack() const { - return TemplateArgument(llvm::makeArrayRef(Arguments, getNumArgs())); + return TemplateArgument(llvm::ArrayRef(Arguments, getNumArgs())); } void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getReplacedParameter(), getArgumentPack()); + Profile(ID, getAssociatedDecl(), getIndex(), getFinal(), getArgumentPack()); } void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID, - const TemplateTypeParmType *Replaced, + const Decl *AssociatedDecl, + unsigned Index, bool Final, const TemplateArgument &ArgPack) { - ID.AddPointer(Replaced); + ID.AddPointer(AssociatedDecl); + ID.AddInteger(Index); + ID.AddBoolean(Final); ID.AddInteger(ArgPack.pack_size()); for (const auto &P : ArgPack.pack_elements()) ID.AddPointer(P.getAsType().getAsOpaquePtr()); @@ -3740,10 +3816,22 @@ TemplateSpecializationType::TemplateSpecializationType( // Store the aliased type if this is a type alias template specialization. if (isTypeAlias()) { auto *Begin = reinterpret_cast<TemplateArgument *>(this + 1); - *reinterpret_cast<QualType*>(Begin + getNumArgs()) = AliasedType; + *reinterpret_cast<QualType *>(Begin + Args.size()) = AliasedType; } } +QualType TemplateSpecializationType::getAliasedType() const { + assert(isTypeAlias() && "not a type alias template specialization"); + return *reinterpret_cast<const QualType *>(template_arguments().end()); +} + +void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Ctx) { + Profile(ID, Template, template_arguments(), Ctx); + if (isTypeAlias()) + getAliasedType().Profile(ID); +} + void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID, TemplateName T, @@ -3780,14 +3868,14 @@ void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID, for (auto typeArg : typeArgs) ID.AddPointer(typeArg.getAsOpaquePtr()); ID.AddInteger(protocols.size()); - for (auto proto : protocols) + for (auto *proto : protocols) ID.AddPointer(proto); ID.AddBoolean(isKindOf); } void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getBaseType(), getTypeArgsAsWritten(), - llvm::makeArrayRef(qual_begin(), getNumProtocols()), + llvm::ArrayRef(qual_begin(), getNumProtocols()), isKindOfTypeAsWritten()); } @@ -3798,13 +3886,13 @@ void ObjCTypeParamType::Profile(llvm::FoldingSetNodeID &ID, ID.AddPointer(OTPDecl); ID.AddPointer(CanonicalType.getAsOpaquePtr()); ID.AddInteger(protocols.size()); - for (auto proto : protocols) + for (auto *proto : protocols) ID.AddPointer(proto); } void ObjCTypeParamType::Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getDecl(), getCanonicalTypeInternal(), - llvm::makeArrayRef(qual_begin(), getNumProtocols())); + llvm::ArrayRef(qual_begin(), getNumProtocols())); } namespace { @@ -4091,8 +4179,7 @@ LinkageInfo Type::getLinkageAndVisibility() const { return LinkageComputer{}.getTypeLinkageAndVisibility(this); } -Optional<NullabilityKind> -Type::getNullability(const ASTContext &Context) const { +std::optional<NullabilityKind> Type::getNullability() const { QualType Type(this, 0); while (const auto *AT = Type->getAs<AttributedType>()) { // Check whether this is an attributed type with nullability @@ -4102,7 +4189,7 @@ Type::getNullability(const ASTContext &Context) const { Type = AT->getEquivalentType(); } - return None; + return std::nullopt; } bool Type::canHaveNullability(bool ResultIfUnknown) const { @@ -4233,8 +4320,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { llvm_unreachable("bad type kind!"); } -llvm::Optional<NullabilityKind> -AttributedType::getImmediateNullability() const { +std::optional<NullabilityKind> AttributedType::getImmediateNullability() const { if (getAttrKind() == attr::TypeNonNull) return NullabilityKind::NonNull; if (getAttrKind() == attr::TypeNullable) @@ -4243,10 +4329,11 @@ AttributedType::getImmediateNullability() const { return NullabilityKind::Unspecified; if (getAttrKind() == attr::TypeNullableResult) return NullabilityKind::NullableResult; - return None; + return std::nullopt; } -Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) { +std::optional<NullabilityKind> +AttributedType::stripOuterNullability(QualType &T) { QualType AttrTy = T; if (auto MacroTy = dyn_cast<MacroQualifiedType>(T)) AttrTy = MacroTy->getUnderlyingType(); @@ -4258,7 +4345,7 @@ Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) { } } - return None; + return std::nullopt; } bool Type::isBlockCompatibleObjCPointerType(ASTContext &ctx) const { @@ -4320,20 +4407,13 @@ bool Type::isObjCARCImplicitlyUnretainedType() const { } bool Type::isObjCNSObjectType() const { - const Type *cur = this; - while (true) { - if (const auto *typedefType = dyn_cast<TypedefType>(cur)) - return typedefType->getDecl()->hasAttr<ObjCNSObjectAttr>(); - - // Single-step desugar until we run out of sugar. - QualType next = cur->getLocallyUnqualifiedSingleStepDesugaredType(); - if (next.getTypePtr() == cur) return false; - cur = next.getTypePtr(); - } + if (const auto *typedefType = getAs<TypedefType>()) + return typedefType->getDecl()->hasAttr<ObjCNSObjectAttr>(); + return false; } bool Type::isObjCIndependentClassType() const { - if (const auto *typedefType = dyn_cast<TypedefType>(this)) + if (const auto *typedefType = getAs<TypedefType>()) return typedefType->getDecl()->hasAttr<ObjCIndependentClassAttr>(); return false; } @@ -4465,7 +4545,8 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, AutoTypeBits.NumArgs = TypeConstraintArgs.size(); this->TypeConstraintConcept = TypeConstraintConcept; if (TypeConstraintConcept) { - TemplateArgument *ArgBuffer = getArgBuffer(); + auto *ArgBuffer = + const_cast<TemplateArgument *>(getTypeConstraintArguments().data()); for (const TemplateArgument &Arg : TypeConstraintArgs) { addDependence( toSyntacticDependence(toTypeDependence(Arg.getDependence()))); @@ -4486,3 +4567,8 @@ void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, for (const TemplateArgument &Arg : Arguments) Arg.Profile(ID, Context); } + +void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) { + Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), + getTypeConstraintConcept(), getTypeConstraintArguments()); +} diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index cf5e2f979230..bcc5a223e6f7 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -194,15 +194,21 @@ SourceLocation TypeLoc::getBeginLoc() const { while (true) { switch (Cur.getTypeLocClass()) { case Elaborated: - LeftMost = Cur; - break; + if (Cur.getLocalSourceRange().getBegin().isValid()) { + LeftMost = Cur; + break; + } + Cur = Cur.getNextTypeLoc(); + if (Cur.isNull()) + break; + continue; case FunctionProto: if (Cur.castAs<FunctionProtoTypeLoc>().getTypePtr() ->hasTrailingReturn()) { LeftMost = Cur; break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case FunctionNoProto: case ConstantArray: case DependentSizedArray: @@ -254,7 +260,7 @@ SourceLocation TypeLoc::getEndLoc() const { // `id` and `id<...>` have no star location. if (Cur.castAs<ObjCObjectPointerTypeLoc>().getStarLoc().isInvalid()) break; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Pointer: case BlockPointer: case MemberPointer: @@ -515,8 +521,8 @@ void TypeOfTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { TypeofLikeTypeLoc<TypeOfTypeLoc, TypeOfType, TypeOfTypeLocInfo> ::initializeLocal(Context, Loc); - this->getLocalData()->UnderlyingTInfo = Context.getTrivialTypeSourceInfo( - getUnderlyingType(), Loc); + this->getLocalData()->UnmodifiedTInfo = + Context.getTrivialTypeSourceInfo(getUnmodifiedType(), Loc); } void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context, @@ -530,6 +536,8 @@ void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context, void ElaboratedTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { + if (isEmpty()) + return; setElaboratedKeywordLoc(Loc); NestedNameSpecifierLocBuilder Builder; Builder.MakeTrivial(Context, getTypePtr()->getQualifier(), Loc); @@ -560,17 +568,14 @@ DependentTemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context, setTemplateNameLoc(Loc); setLAngleLoc(Loc); setRAngleLoc(Loc); - TemplateSpecializationTypeLoc::initializeArgLocs(Context, getNumArgs(), - getTypePtr()->getArgs(), - getArgInfos(), Loc); + TemplateSpecializationTypeLoc::initializeArgLocs( + Context, getTypePtr()->template_arguments(), getArgInfos(), Loc); } -void TemplateSpecializationTypeLoc::initializeArgLocs(ASTContext &Context, - unsigned NumArgs, - const TemplateArgument *Args, - TemplateArgumentLocInfo *ArgInfos, - SourceLocation Loc) { - for (unsigned i = 0, e = NumArgs; i != e; ++i) { +void TemplateSpecializationTypeLoc::initializeArgLocs( + ASTContext &Context, ArrayRef<TemplateArgument> Args, + TemplateArgumentLocInfo *ArgInfos, SourceLocation Loc) { + for (unsigned i = 0, e = Args.size(); i != e; ++i) { switch (Args[i].getKind()) { case TemplateArgument::Null: llvm_unreachable("Impossible TemplateArgument"); @@ -627,9 +632,8 @@ void AutoTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { setRAngleLoc(Loc); setLAngleLoc(Loc); setRParenLoc(Loc); - TemplateSpecializationTypeLoc::initializeArgLocs(Context, getNumArgs(), - getTypePtr()->getArgs(), - getArgInfos(), Loc); + TemplateSpecializationTypeLoc::initializeArgLocs( + Context, getTypePtr()->getTypeConstraintArguments(), getArgInfos(), Loc); setNameLoc(Loc); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 6b13d3806037..5c2464904485 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -22,6 +22,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" +#include "clang/AST/TextNodeDumper.h" #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/ExceptionSpecificationType.h" @@ -32,6 +33,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -47,109 +49,103 @@ using namespace clang; namespace { - /// RAII object that enables printing of the ARC __strong lifetime - /// qualifier. - class IncludeStrongLifetimeRAII { - PrintingPolicy &Policy; - bool Old; - - public: - explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) - : Policy(Policy), Old(Policy.SuppressStrongLifetime) { - if (!Policy.SuppressLifetimeQualifiers) - Policy.SuppressStrongLifetime = false; - } +/// RAII object that enables printing of the ARC __strong lifetime +/// qualifier. +class IncludeStrongLifetimeRAII { + PrintingPolicy &Policy; + bool Old; + +public: + explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressStrongLifetime) { + if (!Policy.SuppressLifetimeQualifiers) + Policy.SuppressStrongLifetime = false; + } - ~IncludeStrongLifetimeRAII() { - Policy.SuppressStrongLifetime = Old; - } - }; + ~IncludeStrongLifetimeRAII() { Policy.SuppressStrongLifetime = Old; } +}; - class ParamPolicyRAII { - PrintingPolicy &Policy; - bool Old; +class ParamPolicyRAII { + PrintingPolicy &Policy; + bool Old; - public: - explicit ParamPolicyRAII(PrintingPolicy &Policy) - : Policy(Policy), Old(Policy.SuppressSpecifiers) { - Policy.SuppressSpecifiers = false; - } +public: + explicit ParamPolicyRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressSpecifiers) { + Policy.SuppressSpecifiers = false; + } - ~ParamPolicyRAII() { - Policy.SuppressSpecifiers = Old; - } - }; + ~ParamPolicyRAII() { Policy.SuppressSpecifiers = Old; } +}; - class DefaultTemplateArgsPolicyRAII { - PrintingPolicy &Policy; - bool Old; +class DefaultTemplateArgsPolicyRAII { + PrintingPolicy &Policy; + bool Old; - public: - explicit DefaultTemplateArgsPolicyRAII(PrintingPolicy &Policy) - : Policy(Policy), Old(Policy.SuppressDefaultTemplateArgs) { - Policy.SuppressDefaultTemplateArgs = false; - } +public: + explicit DefaultTemplateArgsPolicyRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressDefaultTemplateArgs) { + Policy.SuppressDefaultTemplateArgs = false; + } - ~DefaultTemplateArgsPolicyRAII() { - Policy.SuppressDefaultTemplateArgs = Old; - } - }; - - class ElaboratedTypePolicyRAII { - PrintingPolicy &Policy; - bool SuppressTagKeyword; - bool SuppressScope; - - public: - explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { - SuppressTagKeyword = Policy.SuppressTagKeyword; - SuppressScope = Policy.SuppressScope; - Policy.SuppressTagKeyword = true; - Policy.SuppressScope = true; - } + ~DefaultTemplateArgsPolicyRAII() { Policy.SuppressDefaultTemplateArgs = Old; } +}; - ~ElaboratedTypePolicyRAII() { - Policy.SuppressTagKeyword = SuppressTagKeyword; - Policy.SuppressScope = SuppressScope; - } - }; - - class TypePrinter { - PrintingPolicy Policy; - unsigned Indentation; - bool HasEmptyPlaceHolder = false; - bool InsideCCAttribute = false; - - public: - explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) - : Policy(Policy), Indentation(Indentation) {} - - void print(const Type *ty, Qualifiers qs, raw_ostream &OS, - StringRef PlaceHolder); - void print(QualType T, raw_ostream &OS, StringRef PlaceHolder); - - static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier); - void spaceBeforePlaceHolder(raw_ostream &OS); - void printTypeSpec(NamedDecl *D, raw_ostream &OS); - void printTemplateId(const TemplateSpecializationType *T, raw_ostream &OS, - bool FullyQualify); - - void printBefore(QualType T, raw_ostream &OS); - void printAfter(QualType T, raw_ostream &OS); - void AppendScope(DeclContext *DC, raw_ostream &OS, - DeclarationName NameInScope); - void printTag(TagDecl *T, raw_ostream &OS); - void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS); +class ElaboratedTypePolicyRAII { + PrintingPolicy &Policy; + bool SuppressTagKeyword; + bool SuppressScope; + +public: + explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { + SuppressTagKeyword = Policy.SuppressTagKeyword; + SuppressScope = Policy.SuppressScope; + Policy.SuppressTagKeyword = true; + Policy.SuppressScope = true; + } + + ~ElaboratedTypePolicyRAII() { + Policy.SuppressTagKeyword = SuppressTagKeyword; + Policy.SuppressScope = SuppressScope; + } +}; + +class TypePrinter { + PrintingPolicy Policy; + unsigned Indentation; + bool HasEmptyPlaceHolder = false; + bool InsideCCAttribute = false; + +public: + explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) + : Policy(Policy), Indentation(Indentation) {} + + void print(const Type *ty, Qualifiers qs, raw_ostream &OS, + StringRef PlaceHolder); + void print(QualType T, raw_ostream &OS, StringRef PlaceHolder); + + static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier); + void spaceBeforePlaceHolder(raw_ostream &OS); + void printTypeSpec(NamedDecl *D, raw_ostream &OS); + void printTemplateId(const TemplateSpecializationType *T, raw_ostream &OS, + bool FullyQualify); + + void printBefore(QualType T, raw_ostream &OS); + void printAfter(QualType T, raw_ostream &OS); + void AppendScope(DeclContext *DC, raw_ostream &OS, + DeclarationName NameInScope); + void printTag(TagDecl *T, raw_ostream &OS); + void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS); #define ABSTRACT_TYPE(CLASS, PARENT) -#define TYPE(CLASS, PARENT) \ - void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \ - void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS); +#define TYPE(CLASS, PARENT) \ + void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \ + void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS); #include "clang/AST/TypeNodes.inc" - private: - void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); - void printAfter(const Type *ty, Qualifiers qs, raw_ostream &OS); - }; +private: + void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); + void printAfter(const Type *ty, Qualifiers qs, raw_ostream &OS); +}; } // namespace @@ -199,7 +195,7 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, raw_ostream &OS, return; } - SaveAndRestore<bool> PHVal(HasEmptyPlaceHolder, PlaceHolder.empty()); + SaveAndRestore PHVal(HasEmptyPlaceHolder, PlaceHolder.empty()); printBefore(T, Quals, OS); OS << PlaceHolder; @@ -262,7 +258,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::VariableArray: case Type::DependentSizedArray: NeedARCStrongQualifier = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Type::ConstantArray: case Type::IncompleteArray: @@ -323,7 +319,7 @@ void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) { if (Policy.SuppressSpecifiers && T->isSpecifierType()) return; - SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder); + SaveAndRestore PrevPHIsEmpty(HasEmptyPlaceHolder); // Print qualifiers as appropriate. @@ -400,7 +396,7 @@ void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) { void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printBefore(T->getPointeeType(), OS); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. @@ -411,7 +407,7 @@ void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) @@ -421,14 +417,14 @@ void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { void TypePrinter::printBlockPointerBefore(const BlockPointerType *T, raw_ostream &OS) { - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printBefore(T->getPointeeType(), OS); OS << '^'; } void TypePrinter::printBlockPointerAfter(const BlockPointerType *T, raw_ostream &OS) { - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printAfter(T->getPointeeType(), OS); } @@ -443,7 +439,7 @@ static QualType skipTopLevelReferences(QualType T) { void TypePrinter::printLValueReferenceBefore(const LValueReferenceType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); QualType Inner = skipTopLevelReferences(T->getPointeeTypeAsWritten()); printBefore(Inner, OS); // Handle things like 'int (&A)[4];' correctly. @@ -456,7 +452,7 @@ void TypePrinter::printLValueReferenceBefore(const LValueReferenceType *T, void TypePrinter::printLValueReferenceAfter(const LValueReferenceType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); QualType Inner = skipTopLevelReferences(T->getPointeeTypeAsWritten()); // Handle things like 'int (&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. @@ -468,7 +464,7 @@ void TypePrinter::printLValueReferenceAfter(const LValueReferenceType *T, void TypePrinter::printRValueReferenceBefore(const RValueReferenceType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); QualType Inner = skipTopLevelReferences(T->getPointeeTypeAsWritten()); printBefore(Inner, OS); // Handle things like 'int (&&A)[4];' correctly. @@ -481,7 +477,7 @@ void TypePrinter::printRValueReferenceBefore(const RValueReferenceType *T, void TypePrinter::printRValueReferenceAfter(const RValueReferenceType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); QualType Inner = skipTopLevelReferences(T->getPointeeTypeAsWritten()); // Handle things like 'int (&&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. @@ -493,7 +489,7 @@ void TypePrinter::printRValueReferenceAfter(const RValueReferenceType *T, void TypePrinter::printMemberPointerBefore(const MemberPointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printBefore(T->getPointeeType(), OS); // Handle things like 'int (Cls::*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. @@ -510,7 +506,7 @@ void TypePrinter::printMemberPointerBefore(const MemberPointerType *T, void TypePrinter::printMemberPointerAfter(const MemberPointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); // Handle things like 'int (Cls::*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) @@ -852,7 +848,7 @@ void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T, OS << '('; } else { // If needed for precedence reasons, wrap the inner part in grouping parens. - SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + SaveAndRestore PrevPHIsEmpty(HasEmptyPlaceHolder, false); printBefore(T->getReturnType(), OS); if (!PrevPHIsEmpty.get()) OS << '('; @@ -880,7 +876,7 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, // If needed for precedence reasons, wrap the inner part in grouping parens. if (!HasEmptyPlaceHolder) OS << ')'; - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); OS << '('; { @@ -1031,7 +1027,7 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info, void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. - SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + SaveAndRestore PrevPHIsEmpty(HasEmptyPlaceHolder, false); printBefore(T->getReturnType(), OS); if (!PrevPHIsEmpty.get()) OS << '('; @@ -1042,7 +1038,7 @@ void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T, // If needed for precedence reasons, wrap the inner part in grouping parens. if (!HasEmptyPlaceHolder) OS << ')'; - SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); OS << "()"; printFunctionAfter(T->getExtInfo(), OS); @@ -1108,7 +1104,8 @@ void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) {} void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T, raw_ostream &OS) { - OS << "typeof "; + OS << (T->getKind() == TypeOfKind::Unqualified ? "typeof_unqual " + : "typeof "); if (T->getUnderlyingExpr()) T->getUnderlyingExpr()->printPretty(OS, nullptr, Policy); spaceBeforePlaceHolder(OS); @@ -1118,8 +1115,9 @@ void TypePrinter::printTypeOfExprAfter(const TypeOfExprType *T, raw_ostream &OS) {} void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) { - OS << "typeof("; - print(T->getUnderlyingType(), OS, StringRef()); + OS << (T->getKind() == TypeOfKind::Unqualified ? "typeof_unqual(" + : "typeof("); + print(T->getUnmodifiedType(), OS, StringRef()); OS << ')'; spaceBeforePlaceHolder(OS); } @@ -1140,29 +1138,19 @@ void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - switch (T->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - OS << "__underlying_type("; - print(T->getBaseType(), OS, StringRef()); - OS << ')'; - spaceBeforePlaceHolder(OS); - return; - } - - printBefore(T->getBaseType(), OS); + static llvm::DenseMap<int, const char *> Transformation = {{ +#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \ + {UnaryTransformType::Enum, "__" #Trait}, +#include "clang/Basic/TransformTypeTraits.def" + }}; + OS << Transformation[T->getUTTKind()] << '('; + print(T->getBaseType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T, - raw_ostream &OS) { - IncludeStrongLifetimeRAII Strong(Policy); - - switch (T->getUTTKind()) { - case UnaryTransformType::EnumUnderlyingType: - return; - } - - printAfter(T->getBaseType(), OS); -} + raw_ostream &OS) {} void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { // If the type has been deduced, do not print 'auto'. @@ -1470,14 +1458,27 @@ void TypePrinter::printSubstTemplateTypeParmPackBefore( const SubstTemplateTypeParmPackType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - printTemplateTypeParmBefore(T->getReplacedParameter(), OS); + if (const TemplateTypeParmDecl *D = T->getReplacedParameter()) { + if (D && D->isImplicit()) { + if (auto *TC = D->getTypeConstraint()) { + TC->print(OS, Policy); + OS << ' '; + } + OS << "auto"; + } else if (IdentifierInfo *Id = D->getIdentifier()) + OS << (Policy.CleanUglifiedParameters ? Id->deuglifiedName() + : Id->getName()); + else + OS << "type-parameter-" << D->getDepth() << '-' << D->getIndex(); + + spaceBeforePlaceHolder(OS); + } } void TypePrinter::printSubstTemplateTypeParmPackAfter( const SubstTemplateTypeParmPackType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - printTemplateTypeParmAfter(T->getReplacedParameter(), OS); } void TypePrinter::printTemplateId(const TemplateSpecializationType *T, @@ -1675,7 +1676,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // If this is a calling convention attribute, don't print the implicit CC from // the modified type. - SaveAndRestore<bool> MaybeSuppressCC(InsideCCAttribute, T->isCallingConv()); + SaveAndRestore MaybeSuppressCC(InsideCCAttribute, T->isCallingConv()); printAfter(T->getModifiedType(), OS); @@ -1733,6 +1734,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::OpenCLLocalAddressSpace: case attr::OpenCLConstantAddressSpace: case attr::OpenCLGenericAddressSpace: + case attr::HLSLGroupSharedAddressSpace: // FIXME: Update printAttributedBefore to print these once we generate // AttributedType nodes for them. break; @@ -1992,11 +1994,11 @@ static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern, if (!isSubstitutedTemplateArgument(Ctx, Template, PTST->getTemplateName(), Args, Depth)) return false; - if (TemplateArgs.size() != PTST->getNumArgs()) + if (TemplateArgs.size() != PTST->template_arguments().size()) return false; for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) - if (!isSubstitutedTemplateArgument(Ctx, TemplateArgs[I], PTST->getArg(I), - Args, Depth)) + if (!isSubstitutedTemplateArgument( + Ctx, TemplateArgs[I], PTST->template_arguments()[I], Args, Depth)) return false; return true; } @@ -2023,6 +2025,16 @@ static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, } } + if (Arg.getKind() == TemplateArgument::Integral && + Pattern.getKind() == TemplateArgument::Expression) { + Expr const *expr = Pattern.getAsExpr(); + + if (!expr->isValueDependent() && expr->isIntegerConstantExpr(Ctx)) { + return llvm::APSInt::isSameValue(expr->EvaluateKnownConstInt(Ctx), + Arg.getAsIntegral()); + } + } + if (Arg.getKind() != Pattern.getKind()) return false; @@ -2042,9 +2054,7 @@ static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, return false; } -/// Make a best-effort determination of whether the type T can be produced by -/// substituting Args into the default argument of Param. -static bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, +bool clang::isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, const NamedDecl *Param, ArrayRef<TemplateArgument> Args, unsigned Depth) { @@ -2228,6 +2238,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "__uptr __ptr32"; case LangAS::ptr64: return "__ptr64"; + case LangAS::hlsl_groupshared: + return "groupshared"; default: return std::to_string(toTargetAddressSpace(AS)); } diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 3d64cb17fa9c..bc9a83bde8a0 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -670,8 +670,9 @@ CharUnits VCallAndVBaseOffsetBuilder::getCurrentOffsetOffset() const { // Under the relative ABI, the offset widths are 32-bit ints instead of // pointer widths. CharUnits OffsetWidth = Context.toCharUnitsFromBits( - VTables.isRelativeLayout() ? 32 - : Context.getTargetInfo().getPointerWidth(0)); + VTables.isRelativeLayout() + ? 32 + : Context.getTargetInfo().getPointerWidth(LangAS::Default)); CharUnits OffsetOffset = OffsetWidth * OffsetIndex; return OffsetOffset; |