diff options
Diffstat (limited to 'clang/lib/AST')
59 files changed, 1913 insertions, 1344 deletions
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 2d62209bbc28..7173c2a0e1a2 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -903,8 +903,7 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, case APValue::Struct: { Out << '{'; bool First = true; - const RecordDecl *RD = - Ty->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); + const auto *RD = Ty->castAsRecordDecl(); if (unsigned N = getStructNumBases()) { const CXXRecordDecl *CD = cast<CXXRecordDecl>(RD); CXXRecordDecl::base_class_const_iterator BI = CD->bases_begin(); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index bbb957067c4c..ed4c6b0e38be 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -654,9 +654,9 @@ comments::FullComment *ASTContext::getCommentForDecl( // does not have one of its own. QualType QT = TD->getUnderlyingType(); if (const auto *TT = QT->getAs<TagType>()) - if (const Decl *TD = TT->getOriginalDecl()) - if (comments::FullComment *FC = getCommentForDecl(TD, PP)) - return cloneFullComment(FC, D); + if (comments::FullComment *FC = + getCommentForDecl(TT->getOriginalDecl(), PP)) + return cloneFullComment(FC, D); } else if (const auto *IC = dyn_cast<ObjCInterfaceDecl>(D)) { while (IC->getSuperClass()) { @@ -1708,7 +1708,7 @@ void ASTContext::setRelocationInfoForCXXRecord( } static bool primaryBaseHaseAddressDiscriminatedVTableAuthentication( - ASTContext &Context, const CXXRecordDecl *Class) { + const ASTContext &Context, const CXXRecordDecl *Class) { if (!Class->isPolymorphic()) return false; const CXXRecordDecl *BaseType = Context.baseForVTableAuthentication(Class); @@ -1723,7 +1723,8 @@ static bool primaryBaseHaseAddressDiscriminatedVTableAuthentication( return AddressDiscrimination == AuthAttr::AddressDiscrimination; } -ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) { +ASTContext::PointerAuthContent +ASTContext::findPointerAuthContent(QualType T) const { assert(isPointerAuthenticationAvailable()); T = T.getCanonicalType(); @@ -1933,12 +1934,9 @@ TypeInfoChars ASTContext::getTypeInfoDataSizeInChars(QualType T) const { // of a base-class subobject. We decide whether that's possible // during class layout, so here we can just trust the layout results. if (getLangOpts().CPlusPlus) { - if (const auto *RT = T->getAs<RecordType>()) { - const auto *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - if (!RD->isInvalidDecl()) { - const ASTRecordLayout &layout = getASTRecordLayout(RD); - Info.Width = layout.getDataSize(); - } + if (const auto *RD = T->getAsCXXRecordDecl(); RD && !RD->isInvalidDecl()) { + const ASTRecordLayout &layout = getASTRecordLayout(RD); + Info.Width = layout.getDataSize(); } } @@ -2004,8 +2002,7 @@ bool ASTContext::isPromotableIntegerType(QualType T) const { // 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>()) { - const EnumDecl *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *ED = T->getAsEnumDecl()) { if (T->isDependentType() || ED->getPromotionType().isNull() || ED->isScoped()) return false; @@ -2618,10 +2615,10 @@ unsigned ASTContext::getTypeUnadjustedAlign(const Type *T) const { return I->second; unsigned UnadjustedAlign; - if (const auto *RT = T->getAs<RecordType>()) { + if (const auto *RT = T->getAsCanonical<RecordType>()) { const ASTRecordLayout &Layout = getASTRecordLayout(RT->getOriginalDecl()); UnadjustedAlign = toBits(Layout.getUnadjustedAlignment()); - } else if (const auto *ObjCI = T->getAs<ObjCInterfaceType>()) { + } else if (const auto *ObjCI = T->getAsCanonical<ObjCInterfaceType>()) { const ASTRecordLayout &Layout = getASTObjCInterfaceLayout(ObjCI->getDecl()); UnadjustedAlign = toBits(Layout.getUnadjustedAlignment()); } else { @@ -2694,9 +2691,7 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { if (!Target->allowsLargerPreferedTypeAlignment()) return ABIAlign; - if (const auto *RT = T->getAs<RecordType>()) { - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - + if (const auto *RD = T->getAsRecordDecl()) { // When used as part of a typedef, or together with a 'packed' attribute, // the 'aligned' attribute can be used to decrease alignment. Note that the // 'packed' case is already taken into consideration when computing the @@ -2717,11 +2712,8 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { // possible. if (const auto *CT = T->getAs<ComplexType>()) T = CT->getElementType().getTypePtr(); - if (const auto *ET = T->getAs<EnumType>()) - T = ET->getOriginalDecl() - ->getDefinitionOrSelf() - ->getIntegerType() - .getTypePtr(); + if (const auto *ED = T->getAsEnumDecl()) + T = ED->getIntegerType().getTypePtr(); if (T->isSpecificBuiltinType(BuiltinType::Double) || T->isSpecificBuiltinType(BuiltinType::LongLong) || T->isSpecificBuiltinType(BuiltinType::ULongLong) || @@ -2887,12 +2879,10 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, static std::optional<int64_t> getSubobjectSizeInBits(const FieldDecl *Field, const ASTContext &Context, bool CheckIfTriviallyCopyable) { - if (Field->getType()->isRecordType()) { - const RecordDecl *RD = Field->getType()->getAsRecordDecl(); - if (!RD->isUnion()) - return structHasUniqueObjectRepresentations(Context, RD, - CheckIfTriviallyCopyable); - } + if (const auto *RD = Field->getType()->getAsRecordDecl(); + RD && !RD->isUnion()) + return structHasUniqueObjectRepresentations(Context, RD, + CheckIfTriviallyCopyable); // A _BitInt type may not be unique if it has padding bits // but if it is a bitfield the padding bits are not used. @@ -3040,17 +3030,14 @@ bool ASTContext::hasUniqueObjectRepresentations( return true; } - // All other pointers (except __ptrauth pointers) are unique. + // All other pointers are unique. if (Ty->isPointerType()) return !Ty.hasAddressDiscriminatedPointerAuth(); if (const auto *MPT = Ty->getAs<MemberPointerType>()) return !ABI->getMemberPointerInfo(MPT).HasPadding; - if (Ty->isRecordType()) { - const RecordDecl *Record = - Ty->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); - + if (const auto *Record = Ty->getAsRecordDecl()) { if (Record->isInvalidDecl()) return false; @@ -3422,10 +3409,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, // type, or an unsigned integer type. // // So we have to treat enum types as integers. - QualType UnderlyingType = cast<EnumType>(T) - ->getOriginalDecl() - ->getDefinitionOrSelf() - ->getIntegerType(); + QualType UnderlyingType = T->castAsEnumDecl()->getIntegerType(); return encodeTypeForFunctionPointerAuth( Ctx, OS, UnderlyingType.isNull() ? Ctx.IntTy : UnderlyingType); } @@ -3569,8 +3553,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, llvm_unreachable("should never get here"); } case Type::Record: { - const RecordDecl *RD = - T->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); + const RecordDecl *RD = T->castAsCanonical<RecordType>()->getOriginalDecl(); const IdentifierInfo *II = RD->getIdentifier(); // In C++, an immediate typedef of an anonymous struct or union @@ -5333,7 +5316,8 @@ ASTContext::getTypedefType(ElaboratedTypeKeyword Keyword, } llvm::FoldingSetNodeID ID; - TypedefType::Profile(ID, Keyword, Qualifier, Decl, UnderlyingType); + TypedefType::Profile(ID, Keyword, Qualifier, Decl, + *TypeMatchesDeclOrNone ? QualType() : UnderlyingType); void *InsertPos = nullptr; if (FoldingSetPlaceholder<TypedefType> *Placeholder = @@ -5484,18 +5468,15 @@ TagType *ASTContext::getTagTypeInternal(ElaboratedTypeKeyword Keyword, return T; } -static bool getNonInjectedClassName(const TagDecl *&TD) { +static const TagDecl *getNonInjectedClassName(const TagDecl *TD) { if (const auto *RD = dyn_cast<CXXRecordDecl>(TD); - RD && RD->isInjectedClassName()) { - TD = cast<TagDecl>(RD->getDeclContext()); - return true; - } - return false; + RD && RD->isInjectedClassName()) + return cast<TagDecl>(RD->getDeclContext()); + return TD; } CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const { - ::getNonInjectedClassName(TD); - TD = TD->getCanonicalDecl(); + TD = ::getNonInjectedClassName(TD)->getCanonicalDecl(); if (TD->TypeForDecl) return TD->TypeForDecl->getCanonicalTypeUnqualified(); @@ -5511,40 +5492,42 @@ CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const { QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword, NestedNameSpecifier Qualifier, const TagDecl *TD, bool OwnsTag) const { + + const TagDecl *NonInjectedTD = ::getNonInjectedClassName(TD); + bool IsInjected = TD != NonInjectedTD; + ElaboratedTypeKeyword PreferredKeyword = - getLangOpts().CPlusPlus - ? ElaboratedTypeKeyword::None - : KeywordHelpers::getKeywordForTagTypeKind(TD->getTagKind()); + getLangOpts().CPlusPlus ? ElaboratedTypeKeyword::None + : KeywordHelpers::getKeywordForTagTypeKind( + NonInjectedTD->getTagKind()); if (Keyword == PreferredKeyword && !Qualifier && !OwnsTag) { if (const Type *T = TD->TypeForDecl; T && !T->isCanonicalUnqualified()) return QualType(T, 0); - bool IsInjected = ::getNonInjectedClassName(TD); - const Type *CanonicalType = getCanonicalTagType(TD).getTypePtr(); + const Type *CanonicalType = getCanonicalTagType(NonInjectedTD).getTypePtr(); const Type *T = getTagTypeInternal(Keyword, - /*Qualifier=*/std::nullopt, TD, + /*Qualifier=*/std::nullopt, NonInjectedTD, /*OwnsTag=*/false, IsInjected, CanonicalType, /*WithFoldingSetNode=*/false); TD->TypeForDecl = T; return QualType(T, 0); } - bool IsInjected = ::getNonInjectedClassName(TD); - llvm::FoldingSetNodeID ID; - TagTypeFoldingSetPlaceholder::Profile(ID, Keyword, Qualifier, TD, OwnsTag, - IsInjected); + TagTypeFoldingSetPlaceholder::Profile(ID, Keyword, Qualifier, NonInjectedTD, + OwnsTag, IsInjected); void *InsertPos = nullptr; if (TagTypeFoldingSetPlaceholder *T = TagTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(T->getTagType(), 0); - const Type *CanonicalType = getCanonicalTagType(TD).getTypePtr(); - TagType *T = getTagTypeInternal(Keyword, Qualifier, TD, OwnsTag, IsInjected, - CanonicalType, /*WithFoldingSetNode=*/true); + const Type *CanonicalType = getCanonicalTagType(NonInjectedTD).getTypePtr(); + TagType *T = + getTagTypeInternal(Keyword, Qualifier, NonInjectedTD, OwnsTag, IsInjected, + CanonicalType, /*WithFoldingSetNode=*/true); TagTypes.InsertNode(TagTypeFoldingSetPlaceholder::fromTagType(T), InsertPos); return QualType(T, 0); } @@ -8362,8 +8345,8 @@ QualType ASTContext::isPromotableBitField(Expr *E) const { QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { assert(!Promotable.isNull()); assert(isPromotableIntegerType(Promotable)); - if (const auto *ET = Promotable->getAs<EnumType>()) - return ET->getOriginalDecl()->getDefinitionOrSelf()->getPromotionType(); + if (const auto *ED = Promotable->getAsEnumDecl()) + return ED->getPromotionType(); if (const auto *BT = Promotable->getAs<BuiltinType>()) { // C++ [conv.prom]: A prvalue of type char16_t, char32_t, or wchar_t @@ -8582,10 +8565,9 @@ QualType ASTContext::getObjCSuperType() const { } void ASTContext::setCFConstantStringType(QualType T) { - const auto *TD = T->castAs<TypedefType>(); - CFConstantStringTypeDecl = cast<TypedefDecl>(TD->getDecl()); - const auto *TagType = TD->castAs<RecordType>(); - CFConstantStringTagDecl = TagType->getOriginalDecl()->getDefinitionOrSelf(); + const auto *TT = T->castAs<TypedefType>(); + CFConstantStringTypeDecl = cast<TypedefDecl>(TT->getDecl()); + CFConstantStringTagDecl = TT->castAsRecordDecl(); } QualType ASTContext::getBlockDescriptorType() const { @@ -9296,8 +9278,8 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C, llvm_unreachable("invalid BuiltinType::Kind value"); } -static char ObjCEncodingForEnumType(const ASTContext *C, const EnumType *ET) { - EnumDecl *Enum = ET->getOriginalDecl()->getDefinitionOrSelf(); +static char ObjCEncodingForEnumDecl(const ASTContext *C, const EnumDecl *ED) { + EnumDecl *Enum = ED->getDefinitionOrSelf(); // The encoding of an non-fixed enum type is always 'i', regardless of size. if (!Enum->isFixed()) @@ -9340,8 +9322,8 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, S += llvm::utostr(Offset); - if (const auto *ET = T->getAs<EnumType>()) - S += ObjCEncodingForEnumType(Ctx, ET); + if (const auto *ET = T->getAsCanonical<EnumType>()) + S += ObjCEncodingForEnumDecl(Ctx, ET->getOriginalDecl()); else { const auto *BT = T->castAs<BuiltinType>(); S += getObjCEncodingForPrimitiveType(Ctx, BT); @@ -9398,7 +9380,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, if (const auto *BT = dyn_cast<BuiltinType>(CT)) S += getObjCEncodingForPrimitiveType(this, BT); else - S += ObjCEncodingForEnumType(this, cast<EnumType>(CT)); + S += ObjCEncodingForEnumDecl(this, cast<EnumType>(CT)->getOriginalDecl()); return; case Type::Complex: @@ -9465,7 +9447,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, S += '*'; return; } - } else if (const auto *RTy = PointeeTy->getAs<RecordType>()) { + } else if (const auto *RTy = PointeeTy->getAsCanonical<RecordType>()) { const IdentifierInfo *II = RTy->getOriginalDecl()->getIdentifier(); // GCC binary compat: Need to convert "struct objc_class *" to "#". if (II == &Idents.get("objc_class")) { @@ -10457,6 +10439,12 @@ TemplateName ASTContext::getQualifiedTemplateName(NestedNameSpecifier Qualifier, assert(Template.getKind() == TemplateName::Template || Template.getKind() == TemplateName::UsingTemplate); + if (Template.getAsTemplateDecl()->getKind() == Decl::TemplateTemplateParm) { + assert(!Qualifier && "unexpected qualified template template parameter"); + assert(TemplateKeyword == false); + return Template; + } + // FIXME: Canonicalization? llvm::FoldingSetNodeID ID; QualifiedTemplateName::Profile(ID, Qualifier, TemplateKeyword, Template); @@ -11672,9 +11660,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, // Look at the converted type of enum types, since that is the type used // to pass enum values. - if (const auto *Enum = paramTy->getAs<EnumType>()) { - paramTy = - Enum->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType(); + if (const auto *ED = paramTy->getAsEnumDecl()) { + paramTy = ED->getIntegerType(); if (paramTy.isNull()) return {}; } @@ -11832,10 +11819,10 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, if (LHSClass != RHSClass) { // Note that we only have special rules for turning block enum // returns into block int returns, not vice-versa. - if (const auto *ETy = LHS->getAs<EnumType>()) { + if (const auto *ETy = LHS->getAsCanonical<EnumType>()) { return mergeEnumWithInteger(*this, ETy, RHS, false); } - if (const EnumType* ETy = RHS->getAs<EnumType>()) { + if (const EnumType *ETy = RHS->getAsCanonical<EnumType>()) { return mergeEnumWithInteger(*this, ETy, LHS, BlockReturnType); } // allow block pointer type to match an 'id' type. @@ -12265,8 +12252,8 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) { //===----------------------------------------------------------------------===// unsigned ASTContext::getIntWidth(QualType T) const { - if (const auto *ET = T->getAs<EnumType>()) - T = ET->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType(); + if (const auto *ED = T->getAsEnumDecl()) + T = ED->getIntegerType(); if (T->isBooleanType()) return 1; if (const auto *EIT = T->getAs<BitIntType>()) @@ -12291,8 +12278,8 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { // For enums, get the underlying integer type of the enum, and let the general // integer type signchanging code handle it. - if (const auto *ETy = T->getAs<EnumType>()) - T = ETy->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType(); + if (const auto *ED = T->getAsEnumDecl()) + T = ED->getIntegerType(); switch (T->castAs<BuiltinType>()->getKind()) { case BuiltinType::Char_U: @@ -12365,8 +12352,8 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const { // For enums, get the underlying integer type of the enum, and let the general // integer type signchanging code handle it. - if (const auto *ETy = T->getAs<EnumType>()) - T = ETy->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType(); + if (const auto *ED = T->getAsEnumDecl()) + T = ED->getIntegerType(); switch (T->castAs<BuiltinType>()->getKind()) { case BuiltinType::Char_S: @@ -14208,7 +14195,11 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X, FunctionProtoType::ExtProtoInfo EPIX = FX->getExtProtoInfo(), EPIY = FY->getExtProtoInfo(); assert(EPIX.ExtInfo == EPIY.ExtInfo); - assert(EPIX.ExtParameterInfos == EPIY.ExtParameterInfos); + assert(!EPIX.ExtParameterInfos == !EPIY.ExtParameterInfos); + assert(!EPIX.ExtParameterInfos || + llvm::equal( + llvm::ArrayRef(EPIX.ExtParameterInfos, FX->getNumParams()), + llvm::ArrayRef(EPIY.ExtParameterInfos, FY->getNumParams()))); assert(EPIX.RefQualifier == EPIY.RefQualifier); assert(EPIX.TypeQuals == EPIY.TypeQuals); assert(EPIX.Variadic == EPIY.Variadic); @@ -15203,7 +15194,7 @@ StringRef ASTContext::getCUIDHash() const { } const CXXRecordDecl * -ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { +ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) const { assert(ThisClass); assert(ThisClass->isPolymorphic()); const CXXRecordDecl *PrimaryBase = ThisClass; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 6299efaf6bbf..fe7f1e5eb031 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1740,10 +1740,21 @@ ExpectedType ASTNodeImporter::VisitDeducedTemplateSpecializationType( } ExpectedType ASTNodeImporter::VisitTagType(const TagType *T) { - Expected<TagDecl *> ToDeclOrErr = import(T->getOriginalDecl()); + TagDecl *DeclForType = T->getOriginalDecl(); + Expected<TagDecl *> ToDeclOrErr = import(DeclForType); if (!ToDeclOrErr) return ToDeclOrErr.takeError(); + if (DeclForType->isUsed()) { + // If there is a definition of the 'OriginalDecl', it should be imported to + // have all information for the type in the "To" AST. (In some cases no + // other reference may exist to the definition decl and it would not be + // imported otherwise.) + Expected<TagDecl *> ToDefDeclOrErr = import(DeclForType->getDefinition()); + if (!ToDefDeclOrErr) + return ToDefDeclOrErr.takeError(); + } + if (T->isCanonicalUnqualified()) return Importer.getToContext().getCanonicalTagType(*ToDeclOrErr); @@ -7337,18 +7348,28 @@ ExpectedStmt ASTNodeImporter::VisitIndirectGotoStmt(IndirectGotoStmt *S) { ToGotoLoc, ToStarLoc, ToTarget); } +template <typename StmtClass> +static ExpectedStmt ImportLoopControlStmt(ASTNodeImporter &NodeImporter, + ASTImporter &Importer, StmtClass *S) { + Error Err = Error::success(); + auto ToLoc = NodeImporter.importChecked(Err, S->getKwLoc()); + auto ToLabelLoc = S->hasLabelTarget() + ? NodeImporter.importChecked(Err, S->getLabelLoc()) + : SourceLocation(); + auto ToDecl = S->hasLabelTarget() + ? NodeImporter.importChecked(Err, S->getLabelDecl()) + : nullptr; + if (Err) + return std::move(Err); + return new (Importer.getToContext()) StmtClass(ToLoc, ToLabelLoc, ToDecl); +} + ExpectedStmt ASTNodeImporter::VisitContinueStmt(ContinueStmt *S) { - ExpectedSLoc ToContinueLocOrErr = import(S->getContinueLoc()); - if (!ToContinueLocOrErr) - return ToContinueLocOrErr.takeError(); - return new (Importer.getToContext()) ContinueStmt(*ToContinueLocOrErr); + return ImportLoopControlStmt(*this, Importer, S); } ExpectedStmt ASTNodeImporter::VisitBreakStmt(BreakStmt *S) { - auto ToBreakLocOrErr = import(S->getBreakLoc()); - if (!ToBreakLocOrErr) - return ToBreakLocOrErr.takeError(); - return new (Importer.getToContext()) BreakStmt(*ToBreakLocOrErr); + return ImportLoopControlStmt(*this, Importer, S); } ExpectedStmt ASTNodeImporter::VisitReturnStmt(ReturnStmt *S) { diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 6d83de384ee1..1292c30d4758 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -878,10 +878,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, // Treat the enumeration as its underlying type and use the builtin type // class comparison. if (T1->getTypeClass() == Type::Enum) { - T1 = T1->getAs<EnumType>()->getOriginalDecl()->getIntegerType(); + T1 = cast<EnumType>(T1)->getOriginalDecl()->getIntegerType(); assert(T2->isBuiltinType() && !T1.isNull()); // Sanity check } else if (T2->getTypeClass() == Type::Enum) { - T2 = T2->getAs<EnumType>()->getOriginalDecl()->getIntegerType(); + T2 = cast<EnumType>(T2)->getOriginalDecl()->getIntegerType(); assert(T1->isBuiltinType() && !T2.isNull()); // Sanity check } TC = Type::Builtin; diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index e3235d34e230..a21358338250 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -62,7 +62,7 @@ public: OptionScope(Compiler<Emitter> *Ctx, bool NewDiscardResult, bool NewInitializing, bool NewToLValue) : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitializing(Ctx->Initializing), OldToLValue(NewToLValue) { + OldInitializing(Ctx->Initializing), OldToLValue(Ctx->ToLValue) { Ctx->DiscardResult = NewDiscardResult; Ctx->Initializing = NewInitializing; Ctx->ToLValue = NewToLValue; @@ -559,8 +559,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { // Possibly diagnose casts to enum types if the target type does not // have a fixed size. if (Ctx.getLangOpts().CPlusPlus && CE->getType()->isEnumeralType()) { - const auto *ET = CE->getType().getCanonicalType()->castAs<EnumType>(); - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + const auto *ED = CE->getType()->castAsEnumDecl(); if (!ED->isFixed()) { if (!this->emitCheckEnumValue(*FromT, ED, CE)) return false; @@ -943,7 +942,7 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { if (!Result) return false; if (DiscardResult) - return this->emitPop(*T, BO); + return this->emitPopBool(BO); if (T != PT_Bool) return this->emitCast(PT_Bool, *T, BO); return true; @@ -1377,14 +1376,20 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { template <class Emitter> bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); assert(!E->isCommaOp() && "Comma op should be handled in VisitBinaryOperator"); assert(E->getType()->isVectorType()); - assert(E->getLHS()->getType()->isVectorType()); - assert(E->getRHS()->getType()->isVectorType()); + assert(LHS->getType()->isVectorType()); + assert(RHS->getType()->isVectorType()); + + // We can only handle vectors with primitive element types. + if (!canClassify(LHS->getType()->castAs<VectorType>()->getElementType())) + return false; // Prepare storage for result. - if (!Initializing && !E->isCompoundAssignmentOp()) { + if (!Initializing && !E->isCompoundAssignmentOp() && !E->isAssignmentOp()) { UnsignedOrNone LocalIndex = allocateTemporary(E); if (!LocalIndex) return false; @@ -1392,8 +1397,6 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { return false; } - const Expr *LHS = E->getLHS(); - const Expr *RHS = E->getRHS(); const auto *VecTy = E->getType()->getAs<VectorType>(); auto Op = E->isCompoundAssignmentOp() ? BinaryOperator::getOpForCompoundAssignment(E->getOpcode()) @@ -1403,6 +1406,21 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { PrimType RHSElemT = this->classifyVectorElementType(RHS->getType()); PrimType ResultElemT = this->classifyVectorElementType(E->getType()); + if (E->getOpcode() == BO_Assign) { + assert(Ctx.getASTContext().hasSameUnqualifiedType( + LHS->getType()->castAs<VectorType>()->getElementType(), + RHS->getType()->castAs<VectorType>()->getElementType())); + if (!this->visit(LHS)) + return false; + if (!this->visit(RHS)) + return false; + if (!this->emitCopyArray(ElemT, 0, 0, VecTy->getNumElements(), E)) + return false; + if (DiscardResult) + return this->emitPopPtr(E); + return true; + } + // Evaluate LHS and save value to LHSOffset. unsigned LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true); @@ -2248,7 +2266,9 @@ bool Compiler<Emitter>::VisitUnaryExprOrTypeTraitExpr( assert(VAT); if (VAT->getElementType()->isArrayType()) { std::optional<APSInt> Res = - VAT->getSizeExpr()->getIntegerConstantExpr(ASTCtx); + VAT->getSizeExpr() + ? VAT->getSizeExpr()->getIntegerConstantExpr(ASTCtx) + : std::nullopt; if (Res) { if (DiscardResult) return true; @@ -2892,25 +2912,20 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( OptPrimType SubExprT = classify(SubExpr); bool IsStatic = E->getStorageDuration() == SD_Static; if (IsStatic) { - std::optional<unsigned> GlobalIndex = P.createGlobal(E); + + UnsignedOrNone GlobalIndex = P.createGlobal(E); if (!GlobalIndex) return false; const LifetimeExtendedTemporaryDecl *TempDecl = E->getLifetimeExtendedTemporaryDecl(); - if (IsStatic) - assert(TempDecl); + assert(TempDecl); if (SubExprT) { if (!this->visit(SubExpr)) return false; - if (IsStatic) { - if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) - return false; - } else { - if (!this->emitInitGlobal(*SubExprT, *GlobalIndex, E)) - return false; - } + if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) + return false; return this->emitGetPtrGlobal(*GlobalIndex, E); } @@ -2921,9 +2936,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( return false; if (!this->visitInitializer(SubExpr)) return false; - if (IsStatic) - return this->emitInitGlobalTempComp(TempDecl, E); - return true; + return this->emitInitGlobalTempComp(TempDecl, E); } // For everyhing else, use local variables. @@ -2989,7 +3002,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { if (T && !E->isLValue()) return this->delegate(Init); - std::optional<unsigned> GlobalIndex = P.createGlobal(E); + UnsignedOrNone GlobalIndex = P.createGlobal(E); if (!GlobalIndex) return false; @@ -3333,7 +3346,7 @@ bool Compiler<Emitter>::VisitSourceLocExpr(const SourceLocExpr *E) { auto *UGCD = cast<UnnamedGlobalConstantDecl>(BaseDecl); - std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(UGCD); + UnsignedOrNone GlobalIndex = P.getOrCreateGlobal(UGCD); if (!GlobalIndex) return false; @@ -3856,7 +3869,7 @@ bool Compiler<Emitter>::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { if (!RD->isCompleteDefinition()) return this->emitDummyPtr(GuidDecl, E); - std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(GuidDecl); + UnsignedOrNone GlobalIndex = P.getOrCreateGlobal(GuidDecl); if (!GlobalIndex) return false; if (!this->emitGetPtrGlobal(*GlobalIndex, E)) @@ -3879,6 +3892,8 @@ bool Compiler<Emitter>::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { template <class Emitter> bool Compiler<Emitter>::VisitRequiresExpr(const RequiresExpr *E) { assert(classifyPrim(E->getType()) == PT_Bool); + if (E->isValueDependent()) + return false; if (DiscardResult) return true; return this->emitConstBool(E->isSatisfied(), E); @@ -4169,6 +4184,31 @@ template <class Emitter> bool Compiler<Emitter>::delegate(const Expr *E) { return this->Visit(E); } +static const Expr *stripCheckedDerivedToBaseCasts(const Expr *E) { + if (const auto *PE = dyn_cast<ParenExpr>(E)) + return stripCheckedDerivedToBaseCasts(PE->getSubExpr()); + + if (const auto *CE = dyn_cast<CastExpr>(E); + CE && + (CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_NoOp)) + return stripCheckedDerivedToBaseCasts(CE->getSubExpr()); + + return E; +} + +static const Expr *stripDerivedToBaseCasts(const Expr *E) { + if (const auto *PE = dyn_cast<ParenExpr>(E)) + return stripDerivedToBaseCasts(PE->getSubExpr()); + + if (const auto *CE = dyn_cast<CastExpr>(E); + CE && (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp)) + return stripDerivedToBaseCasts(CE->getSubExpr()); + + return E; +} + template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) { if (E->getType().isNull()) return false; @@ -4177,9 +4217,8 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) { return this->discard(E); // Create local variable to hold the return value. - if (!E->isGLValue() && !E->getType()->isAnyComplexType() && - !canClassify(E->getType())) { - UnsignedOrNone LocalIndex = allocateLocal(E); + if (!E->isGLValue() && !canClassify(E->getType())) { + UnsignedOrNone LocalIndex = allocateLocal(stripDerivedToBaseCasts(E)); if (!LocalIndex) return false; @@ -4608,8 +4647,8 @@ UnsignedOrNone Compiler<Emitter>::allocateTemporary(const Expr *E) { template <class Emitter> const RecordType *Compiler<Emitter>::getRecordTy(QualType Ty) { if (const PointerType *PT = dyn_cast<PointerType>(Ty)) - return PT->getPointeeType()->getAs<RecordType>(); - return Ty->getAs<RecordType>(); + return PT->getPointeeType()->getAsCanonical<RecordType>(); + return Ty->getAsCanonical<RecordType>(); } template <class Emitter> Record *Compiler<Emitter>::getRecord(QualType Ty) { @@ -4822,7 +4861,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, DeclScope<Emitter> LocalScope(this, VD); // We've already seen and initialized this global. - if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) { + if (UnsignedOrNone GlobalIndex = P.getGlobal(VD)) { if (P.getPtrGlobal(*GlobalIndex).isInitialized()) return checkDecl(); @@ -4831,7 +4870,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, return Init && checkDecl() && initGlobal(*GlobalIndex); } - std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init); + UnsignedOrNone GlobalIndex = P.createGlobal(VD, Init); if (!GlobalIndex) return false; @@ -5057,18 +5096,6 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E, return true; } -static const Expr *stripDerivedToBaseCasts(const Expr *E) { - if (const auto *PE = dyn_cast<ParenExpr>(E)) - return stripDerivedToBaseCasts(PE->getSubExpr()); - - if (const auto *CE = dyn_cast<CastExpr>(E); - CE && - (CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_NoOp)) - return stripDerivedToBaseCasts(CE->getSubExpr()); - - return E; -} - template <class Emitter> bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const FunctionDecl *FuncDecl = E->getDirectCallee(); @@ -5173,7 +5200,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const auto *InstancePtr = MC->getImplicitObjectArgument(); if (isa_and_nonnull<CXXDestructorDecl>(CompilingFunction) || isa_and_nonnull<CXXConstructorDecl>(CompilingFunction)) { - const auto *Stripped = stripDerivedToBaseCasts(InstancePtr); + const auto *Stripped = stripCheckedDerivedToBaseCasts(InstancePtr); if (isa<CXXThisExpr>(Stripped)) { FuncDecl = cast<CXXMethodDecl>(FuncDecl)->getCorrespondingMethodInClass( @@ -5228,6 +5255,12 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const Function *Func = getFunction(FuncDecl); if (!Func) return false; + + // In error cases, the function may be called with fewer arguments than + // parameters. + if (E->getNumArgs() < Func->getNumWrittenParams()) + return false; + assert(HasRVO == Func->hasRVO()); bool HasQualifier = false; @@ -6244,26 +6277,21 @@ bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) { // First, destroy all fields. for (const Record::Field &Field : llvm::reverse(R->fields())) { const Descriptor *D = Field.Desc; - if (!D->isPrimitive() && !D->isPrimitiveArray()) { - if (!this->emitGetPtrField(Field.Offset, SourceInfo{})) - return false; - if (!this->emitDestruction(D, SourceInfo{})) - return false; - if (!this->emitPopPtr(SourceInfo{})) - return false; - } + if (D->hasTrivialDtor()) + continue; + if (!this->emitGetPtrField(Field.Offset, SourceInfo{})) + return false; + if (!this->emitDestructionPop(D, SourceInfo{})) + return false; } } for (const Record::Base &Base : llvm::reverse(R->bases())) { - if (Base.R->isAnonymousUnion()) + if (Base.R->hasTrivialDtor()) continue; - if (!this->emitGetPtrBase(Base.Offset, SourceInfo{})) return false; - if (!this->emitRecordDestruction(Base.R, {})) - return false; - if (!this->emitPopPtr(SourceInfo{})) + if (!this->emitRecordDestructionPop(Base.R, {})) return false; } @@ -6775,7 +6803,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { return F && this->emitGetFnPtr(F, E); } if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(D)) { - if (std::optional<unsigned> Index = P.getOrCreateGlobal(D)) { + if (UnsignedOrNone Index = P.getOrCreateGlobal(D)) { if (!this->emitGetPtrGlobal(*Index, E)) return false; if (OptPrimType T = classify(E->getType())) { @@ -7160,71 +7188,56 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS, /// on the stack. /// Emit destruction of record types (or arrays of record types). template <class Emitter> -bool Compiler<Emitter>::emitRecordDestruction(const Record *R, SourceInfo Loc) { +bool Compiler<Emitter>::emitRecordDestructionPop(const Record *R, + SourceInfo Loc) { assert(R); - assert(!R->isAnonymousUnion()); + assert(!R->hasTrivialDtor()); const CXXDestructorDecl *Dtor = R->getDestructor(); - if (!Dtor || Dtor->isTrivial()) - return true; - assert(Dtor); const Function *DtorFunc = getFunction(Dtor); if (!DtorFunc) return false; assert(DtorFunc->hasThisPointer()); assert(DtorFunc->getNumParams() == 1); - if (!this->emitDupPtr(Loc)) - return false; return this->emitCall(DtorFunc, 0, Loc); } /// When calling this, we have a pointer of the local-to-destroy /// on the stack. /// Emit destruction of record types (or arrays of record types). template <class Emitter> -bool Compiler<Emitter>::emitDestruction(const Descriptor *Desc, - SourceInfo Loc) { +bool Compiler<Emitter>::emitDestructionPop(const Descriptor *Desc, + SourceInfo Loc) { assert(Desc); - assert(!Desc->isPrimitive()); - assert(!Desc->isPrimitiveArray()); + assert(!Desc->hasTrivialDtor()); // Arrays. if (Desc->isArray()) { const Descriptor *ElemDesc = Desc->ElemDesc; assert(ElemDesc); - // Don't need to do anything for these. - if (ElemDesc->isPrimitiveArray()) - return true; + unsigned N = Desc->getNumElems(); + if (N == 0) + return this->emitPopPtr(Loc); - // If this is an array of record types, check if we need - // to call the element destructors at all. If not, try - // to save the work. - if (const Record *ElemRecord = ElemDesc->ElemRecord) { - if (const CXXDestructorDecl *Dtor = ElemRecord->getDestructor(); - !Dtor || Dtor->isTrivial()) - return true; - } - - if (unsigned N = Desc->getNumElems()) { - for (ssize_t I = N - 1; I >= 0; --I) { - if (!this->emitConstUint64(I, Loc)) - return false; - if (!this->emitArrayElemPtrUint64(Loc)) - return false; - if (!this->emitDestruction(ElemDesc, Loc)) - return false; - if (!this->emitPopPtr(Loc)) - return false; - } + for (ssize_t I = N - 1; I >= 1; --I) { + if (!this->emitConstUint64(I, Loc)) + return false; + if (!this->emitArrayElemPtrUint64(Loc)) + return false; + if (!this->emitDestructionPop(ElemDesc, Loc)) + return false; } - return true; + // Last iteration, removes the instance pointer from the stack. + if (!this->emitConstUint64(0, Loc)) + return false; + if (!this->emitArrayElemPtrPopUint64(Loc)) + return false; + return this->emitDestructionPop(ElemDesc, Loc); } assert(Desc->ElemRecord); - if (Desc->ElemRecord->isAnonymousUnion()) - return true; - - return this->emitRecordDestruction(Desc->ElemRecord, Loc); + assert(!Desc->ElemRecord->hasTrivialDtor()); + return this->emitRecordDestructionPop(Desc->ElemRecord, Loc); } /// Create a dummy pointer for the given decl (or expr) and diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 475faee4d3fd..bb8c66042731 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -391,8 +391,8 @@ private: bool emitComplexBoolCast(const Expr *E); bool emitComplexComparison(const Expr *LHS, const Expr *RHS, const BinaryOperator *E); - bool emitRecordDestruction(const Record *R, SourceInfo Loc); - bool emitDestruction(const Descriptor *Desc, SourceInfo Loc); + bool emitRecordDestructionPop(const Record *R, SourceInfo Loc); + bool emitDestructionPop(const Descriptor *Desc, SourceInfo Loc); bool emitDummyPtr(const DeclTy &D, const Expr *E); bool emitFloat(const APFloat &F, const Expr *E); unsigned collectBaseOffset(const QualType BaseType, @@ -587,11 +587,9 @@ public: if (!this->Ctx->emitGetPtrLocal(Local.Offset, E)) return false; - if (!this->Ctx->emitDestruction(Local.Desc, Local.Desc->getLoc())) + if (!this->Ctx->emitDestructionPop(Local.Desc, Local.Desc->getLoc())) return false; - if (!this->Ctx->emitPopPtr(E)) - return false; removeIfStoredOpaqueValue(Local); } return true; diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index 36eb7607e70b..859899668146 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -91,7 +91,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -121,7 +121,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -153,7 +153,7 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -364,8 +364,7 @@ OptPrimType Context::classify(QualType T) const { return integralTypeToPrimTypeU(BT->getNumBits()); } - if (const auto *ET = T->getAs<EnumType>()) { - const auto *D = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *D = T->getAsEnumDecl()) { if (!D->isComplete()) return std::nullopt; return classify(D->getIntegerType()); diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 9ecc7b673cf2..0a819599287e 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -50,14 +50,6 @@ static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) { } template <typename T> -static void moveTy(Block *, std::byte *Src, std::byte *Dst, - const Descriptor *) { - auto *SrcPtr = reinterpret_cast<T *>(Src); - auto *DstPtr = reinterpret_cast<T *>(Dst); - new (DstPtr) T(std::move(*SrcPtr)); -} - -template <typename T> static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool, const Descriptor *D) { new (Ptr) InitMapPtr(std::nullopt); @@ -85,28 +77,6 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) { } } -template <typename T> -static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst, - const Descriptor *D) { - InitMapPtr &SrcIMP = *reinterpret_cast<InitMapPtr *>(Src); - if (SrcIMP) { - // We only ever invoke the moveFunc when moving block contents to a - // DeadBlock. DeadBlocks don't need InitMaps, so we destroy them here. - SrcIMP = std::nullopt; - } - Src += sizeof(InitMapPtr); - Dst += sizeof(InitMapPtr); - if constexpr (!needsCtor<T>()) { - std::memcpy(Dst, Src, D->getNumElems() * D->getElemSize()); - } else { - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - auto *SrcPtr = &reinterpret_cast<T *>(Src)[I]; - auto *DstPtr = &reinterpret_cast<T *>(Dst)[I]; - new (DstPtr) T(std::move(*SrcPtr)); - } - } -} - static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, bool IsVolatile, bool IsActive, bool InUnion, const Descriptor *D) { @@ -144,12 +114,14 @@ static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) { D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); unsigned ElemOffset = 0; - for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { + auto Dtor = D->ElemDesc->DtorFn; + assert(Dtor && + "a composite array without an elem dtor shouldn't have a dtor itself"); + for (unsigned I = 0; I != NumElems; ++I, ElemOffset += ElemSize) { auto *ElemPtr = Ptr + ElemOffset; auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1); - if (auto Fn = D->ElemDesc->DtorFn) - Fn(B, ElemLoc, D->ElemDesc); + Dtor(B, ElemLoc, D->ElemDesc); } } @@ -246,34 +218,59 @@ static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) { destroyBase(B, Ptr, F.Desc, F.Offset); } -static BlockCtorFn getCtorPrim(PrimType Type) { - // Floating types are special. They are primitives, but need their - // constructor called. - if (Type == PT_Float) +/// Whether a record needs its descriptor dtor function called. +static bool needsRecordDtor(const Record *R) { + for (const auto &B : R->bases()) { + if (B.Desc->DtorFn) + return true; + } + + for (const auto &F : R->fields()) { + if (F.Desc->DtorFn) + return true; + } + + for (const auto &V : R->virtual_bases()) { + if (V.Desc->DtorFn) + return true; + } + return false; +} + +static BlockCtorFn getCtorPrim(PrimType T) { + switch (T) { + case PT_Float: return ctorTy<PrimConv<PT_Float>::T>; - if (Type == PT_IntAP) + case PT_IntAP: return ctorTy<PrimConv<PT_IntAP>::T>; - if (Type == PT_IntAPS) + case PT_IntAPS: return ctorTy<PrimConv<PT_IntAPS>::T>; - if (Type == PT_MemberPtr) + case PT_Ptr: + return ctorTy<PrimConv<PT_Ptr>::T>; + case PT_MemberPtr: return ctorTy<PrimConv<PT_MemberPtr>::T>; - - COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); + default: + return nullptr; + } + llvm_unreachable("Unhandled PrimType"); } -static BlockDtorFn getDtorPrim(PrimType Type) { - // Floating types are special. They are primitives, but need their - // destructor called, since they might allocate memory. - if (Type == PT_Float) +static BlockDtorFn getDtorPrim(PrimType T) { + switch (T) { + case PT_Float: return dtorTy<PrimConv<PT_Float>::T>; - if (Type == PT_IntAP) + case PT_IntAP: return dtorTy<PrimConv<PT_IntAP>::T>; - if (Type == PT_IntAPS) + case PT_IntAPS: return dtorTy<PrimConv<PT_IntAPS>::T>; - if (Type == PT_MemberPtr) + case PT_Ptr: + return dtorTy<PrimConv<PT_Ptr>::T>; + case PT_MemberPtr: return dtorTy<PrimConv<PT_MemberPtr>::T>; - - COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); + default: + return nullptr; + } + llvm_unreachable("Unhandled PrimType"); } static BlockCtorFn getCtorArrayPrim(PrimType Type) { @@ -336,7 +333,7 @@ Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), - DtorFn(dtorArrayDesc) { + DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) { assert(Source && "Missing source"); } @@ -347,7 +344,7 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, Size(UnknownSizeMark), MDSize(MD.value_or(0)), AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc) { + CtorFn(ctorArrayDesc), DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) { assert(Source && "Missing source"); } @@ -359,7 +356,7 @@ Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsVolatile(IsVolatile), CtorFn(ctorRecord), - DtorFn(dtorRecord) { + DtorFn(needsRecordDtor(R) ? dtorRecord : nullptr) { assert(Source && "Missing source"); } @@ -460,8 +457,7 @@ bool Descriptor::hasTrivialDtor() const { if (isRecord()) { assert(ElemRecord); - const CXXDestructorDecl *Dtor = ElemRecord->getDestructor(); - return !Dtor || Dtor->isTrivial(); + return ElemRecord->hasTrivialDtor(); } if (!ElemDesc) diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index 58e6d1a722af..ab3b9f7c3b1d 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -44,7 +44,20 @@ inline static std::string printArg(Program &P, CodePtr &OpPC) { std::string Result; llvm::raw_string_ostream SS(Result); auto Arg = OpPC.read<T>(); - SS << Arg; + // Make sure we print the integral value of chars. + if constexpr (std::is_integral_v<T>) { + if constexpr (sizeof(T) == 1) { + if constexpr (std::is_signed_v<T>) + SS << static_cast<int32_t>(Arg); + else + SS << static_cast<uint32_t>(Arg); + } else { + SS << Arg; + } + } else { + SS << Arg; + } + return Result; } } @@ -549,41 +562,17 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { } LLVM_DUMP_METHOD void EvaluationResult::dump() const { - assert(Ctx); auto &OS = llvm::errs(); - const ASTContext &ASTCtx = Ctx->getASTContext(); - switch (Kind) { - case Empty: + if (empty()) { OS << "Empty\n"; - break; - case RValue: - OS << "RValue: "; - std::get<APValue>(Value).dump(OS, ASTCtx); - break; - case LValue: { - assert(Source); - QualType SourceType; - if (const auto *D = dyn_cast<const Decl *>(Source)) { - if (const auto *VD = dyn_cast<ValueDecl>(D)) - SourceType = VD->getType(); - } else if (const auto *E = dyn_cast<const Expr *>(Source)) { - SourceType = E->getType(); - } - - OS << "LValue: "; - if (const auto *P = std::get_if<Pointer>(&Value)) - P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope - FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); - OS << "\n"; - break; - } - case Invalid: + } else if (isInvalid()) { OS << "Invalid\n"; - break; - case Valid: - OS << "Valid\n"; - break; + } else { + OS << "Value: "; +#ifndef NDEBUG + assert(Ctx); + Value.dump(OS, Ctx->getASTContext()); +#endif } } diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 1ebadae811bd..d0aa8d8df236 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -184,7 +184,7 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { return true; using T = typename PrimConv<OpType>::T; - EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); return true; } @@ -195,7 +195,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (Ptr.isFunctionPointer()) { - EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } @@ -213,6 +213,9 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { if (!Ptr.isZero() && !Ptr.isDereferencable()) return false; + if (Ptr.pointsToStringLiteral() && Ptr.isArrayRoot()) + return false; + if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC, Ptr)) return false; @@ -224,7 +227,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { if (std::optional<APValue> V = Ptr.toRValue(Ctx, EvalResult.getSourceType())) { - EvalResult.setValue(*V); + EvalResult.takeValue(std::move(*V)); } else { return false; } @@ -233,14 +236,14 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { // the result, even if the pointer is dead. // This will later be diagnosed by CheckLValueConstantExpression. if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) { - EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } if (!Ptr.isLive() && !Ptr.isTemporary()) return false; - EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); } return true; @@ -261,7 +264,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { if (std::optional<APValue> APV = Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) { - EvalResult.setValue(*APV); + EvalResult.takeValue(std::move(*APV)); return true; } @@ -328,7 +331,7 @@ bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { /// This is what we do here. void EvalEmitter::updateGlobalTemporaries() { for (const auto &[E, Temp] : S.SeenGlobalTemporaries) { - if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) { + if (UnsignedOrNone GlobalIndex = P.getGlobal(E)) { const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex); APValue *Cached = Temp->getOrCreateValue(true); diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index b11531f4296a..ba818788d702 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -8,6 +8,7 @@ #include "EvaluationResult.h" #include "InterpState.h" +#include "Pointer.h" #include "Record.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" @@ -16,41 +17,6 @@ namespace clang { namespace interp { -APValue EvaluationResult::toAPValue() const { - assert(!empty()); - switch (Kind) { - case LValue: - // Either a pointer or a function pointer. - if (const auto *P = std::get_if<Pointer>(&Value)) - return P->toAPValue(Ctx->getASTContext()); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) - return FP->toAPValue(Ctx->getASTContext()); - else - llvm_unreachable("Unhandled LValue type"); - break; - case RValue: - return std::get<APValue>(Value); - case Valid: - return APValue(); - default: - llvm_unreachable("Unhandled result kind?"); - } -} - -std::optional<APValue> EvaluationResult::toRValue() const { - if (Kind == RValue) - return toAPValue(); - - assert(Kind == LValue); - - // We have a pointer and want an RValue. - if (const auto *P = std::get_if<Pointer>(&Value)) - return P->toRValue(*Ctx, getSourceType()); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope - return FP->toAPValue(Ctx->getASTContext()); - llvm_unreachable("Unhandled lvalue kind"); -} - static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, const FieldDecl *SubObjDecl) { assert(SubObjDecl && "Subobject declaration does not exist"); @@ -66,8 +32,12 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const ConstantArrayType *CAT) { - bool Result = true; size_t NumElems = CAT->getZExtSize(); + + if (NumElems == 0) + return true; + + bool Result = true; QualType ElemType = CAT->getElementType(); if (ElemType->isRecordType()) { @@ -82,8 +52,18 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT); } } else { + // Primitive arrays. + if (S.getContext().canClassify(ElemType)) { + if (BasePtr.allElementsInitialized()) { + return true; + } else { + DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); + return false; + } + } + for (size_t I = 0; I != NumElems; ++I) { - if (!BasePtr.atIndex(I).isInitialized()) { + if (!BasePtr.isElementInitialized(I)) { DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); Result = false; } @@ -178,8 +158,8 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S, static void collectBlocks(const Pointer &Ptr, llvm::SetVector<const Block *> &Blocks) { auto isUsefulPtr = [](const Pointer &P) -> bool { - return P.isLive() && !P.isZero() && !P.isDummy() && P.isDereferencable() && - !P.isUnknownSizeArray() && !P.isOnePastEnd(); + return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() && + P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd(); }; if (!isUsefulPtr(Ptr)) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.h b/clang/lib/AST/ByteCode/EvaluationResult.h index 3b6c65eff1ef..c296cc98ca37 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.h +++ b/clang/lib/AST/ByteCode/EvaluationResult.h @@ -9,23 +9,22 @@ #ifndef LLVM_CLANG_AST_INTERP_EVALUATION_RESULT_H #define LLVM_CLANG_AST_INTERP_EVALUATION_RESULT_H -#include "FunctionPointer.h" -#include "Pointer.h" #include "clang/AST/APValue.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" -#include <optional> -#include <variant> namespace clang { namespace interp { class EvalEmitter; class Context; +class Pointer; +class SourceInfo; +class InterpState; /// Defines the result of an evaluation. /// -/// The result might be in different forms--one of the pointer types, -/// an APValue, or nothing. +/// The Kind defined if the evaluation was invalid, valid (but empty, e.g. for +/// void expressions) or if we have a valid evaluation result. /// /// We use this class to inspect and diagnose the result, as well as /// convert it to the requested form. @@ -33,8 +32,6 @@ class EvaluationResult final { public: enum ResultKind { Empty, // Initial state. - LValue, // Result is an lvalue/pointer. - RValue, // Result is an rvalue. Invalid, // Result is invalid. Valid, // Result is valid and empty. }; @@ -42,29 +39,18 @@ public: using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>; private: +#ifndef NDEBUG const Context *Ctx = nullptr; - std::variant<std::monostate, Pointer, FunctionPointer, APValue> Value; +#endif + APValue Value; ResultKind Kind = Empty; - DeclTy Source = nullptr; // Currently only needed for dump(). - - EvaluationResult(ResultKind Kind) : Kind(Kind) { - // Leave everything empty. Can be used as an - // error marker or for void return values. - assert(Kind == Valid || Kind == Invalid); - } + DeclTy Source = nullptr; void setSource(DeclTy D) { Source = D; } - void setValue(const APValue &V) { - // V could still be an LValue. + void takeValue(APValue &&V) { assert(empty()); Value = std::move(V); - Kind = RValue; - } - void setFunctionPointer(const FunctionPointer &P) { - assert(empty()); - Value = P; - Kind = LValue; } void setInvalid() { // We are NOT asserting empty() here, since setting it to invalid @@ -77,22 +63,23 @@ private: } public: +#ifndef NDEBUG EvaluationResult(const Context *Ctx) : Ctx(Ctx) {} +#else + EvaluationResult(const Context *Ctx) {} +#endif bool empty() const { return Kind == Empty; } bool isInvalid() const { return Kind == Invalid; } - bool isLValue() const { return Kind == LValue; } - bool isRValue() const { return Kind == RValue; } - bool isPointer() const { return std::holds_alternative<Pointer>(Value); } - /// Returns an APValue for the evaluation result. The returned - /// APValue might be an LValue or RValue. - APValue toAPValue() const; + /// Returns an APValue for the evaluation result. + APValue toAPValue() const { + assert(!empty()); + assert(!isInvalid()); + return Value; + } - /// If the result is an LValue, convert that to an RValue - /// and return it. This may fail, e.g. if the result is an - /// LValue and we can't read from it. - std::optional<APValue> toRValue() const; + APValue stealAPValue() { return std::move(Value); } /// Check that all subobjects of the given pointer have been initialized. bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const; @@ -105,7 +92,7 @@ public: if (const auto *D = dyn_cast_if_present<ValueDecl>(Source.dyn_cast<const Decl *>())) return D->getType(); - else if (const auto *E = Source.dyn_cast<const Expr *>()) + if (const auto *E = Source.dyn_cast<const Expr *>()) return E->getType(); return QualType(); } diff --git a/clang/lib/AST/ByteCode/Integral.h b/clang/lib/AST/ByteCode/Integral.h index af5cd2d13ecc..131802439f0c 100644 --- a/clang/lib/AST/ByteCode/Integral.h +++ b/clang/lib/AST/ByteCode/Integral.h @@ -318,6 +318,11 @@ private: 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 if constexpr (sizeof(T) < sizeof(int)) { + // Silly integer promotion rules will convert both A and B to int, + // even it T is unsigned. Prevent that by manually casting to uint first. + R = static_cast<T>(static_cast<unsigned>(A) * static_cast<unsigned>(B)); + return false; } else { R = A * B; return false; diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 796c24e9071e..b64ed8c2fb49 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -792,6 +792,7 @@ bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) { bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { + // Block pointers are the only ones we can actually read from. if (!Ptr.isBlockPointer()) { if (Ptr.isZero()) { const auto &Src = S.Current->getSource(OpPC); @@ -804,7 +805,6 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } - // Block pointers are the only ones we can actually read from. if (!Ptr.block()->isAccessible()) { if (!CheckLive(S, OpPC, Ptr, AK)) return false; @@ -812,8 +812,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; if (!CheckDummy(S, OpPC, Ptr.block(), AK)) return false; - if (!CheckWeak(S, OpPC, Ptr.block())) - return false; + return CheckWeak(S, OpPC, Ptr.block()); } if (!CheckConstant(S, OpPC, Ptr)) @@ -870,7 +869,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { } bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!Ptr.isBlockPointer()) + if (!Ptr.isBlockPointer() || Ptr.isZero()) return false; if (!Ptr.block()->isAccessible()) { @@ -1207,17 +1206,15 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC, } // Destructor of this record. - if (const CXXDestructorDecl *Dtor = R->getDestructor(); - Dtor && !Dtor->isTrivial()) { - const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor); - if (!DtorFunc) - return false; + const CXXDestructorDecl *Dtor = R->getDestructor(); + assert(Dtor); + assert(!Dtor->isTrivial()); + const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor); + if (!DtorFunc) + return false; - S.Stk.push<Pointer>(BasePtr); - if (!Call(S, OpPC, DtorFunc, 0)) - return false; - } - return true; + S.Stk.push<Pointer>(BasePtr); + return Call(S, OpPC, DtorFunc, 0); } static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { @@ -1229,6 +1226,9 @@ static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { assert(Desc->isRecord() || Desc->isCompositeArray()); + if (Desc->hasTrivialDtor()) + return true; + if (Desc->isCompositeArray()) { unsigned N = Desc->getNumElems(); if (N == 0) @@ -2117,8 +2117,8 @@ bool DiagTypeid(InterpState &S, CodePtr OpPC) { bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS, const Pointer &RHS) { - unsigned LHSOffset = LHS.getIndex(); - unsigned RHSOffset = RHS.getIndex(); + unsigned LHSOffset = LHS.isOnePastEnd() ? LHS.getNumElems() : LHS.getIndex(); + unsigned RHSOffset = RHS.isOnePastEnd() ? RHS.getNumElems() : RHS.getIndex(); unsigned LHSLength = (LHS.getNumElems() - 1) * LHS.elemSize(); unsigned RHSLength = (RHS.getNumElems() - 1) * RHS.elemSize(); diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 92e60b6b88e6..2da220237803 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -3490,7 +3490,15 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source, S.Stk.push<Pointer>(0, nullptr); return true; } - assert(NumElements.isPositive()); + if (NumElements.isNegative()) { + if (!IsNoThrow) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_negative) + << NumElements.toDiagnosticString(S.getASTContext()); + return false; + } + S.Stk.push<Pointer>(0, nullptr); + return true; + } if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements))) return false; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 2e28814abfdd..d418e0ac5d09 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -141,6 +141,22 @@ static void diagnoseNonConstexprBuiltin(InterpState &S, CodePtr OpPC, S.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr); } +static llvm::APSInt convertBoolVectorToInt(const Pointer &Val) { + assert(Val.getFieldDesc()->isPrimitiveArray() && + Val.getFieldDesc()->getElemQualType()->isBooleanType() && + "Not a boolean vector"); + unsigned NumElems = Val.getNumElems(); + + // Each element is one bit, so create an integer with NumElts bits. + llvm::APSInt Result(NumElems, 0); + for (unsigned I = 0; I != NumElems; ++I) { + if (Val.elem<bool>(I)) + Result.setBit(I); + } + + return Result; +} + static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { @@ -643,8 +659,14 @@ static bool interp__builtin_abs(InterpState &S, CodePtr OpPC, static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { - PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); - APSInt Val = popToAPSInt(S.Stk, ArgT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); + Val = popToAPSInt(S.Stk, ArgT); + } pushInteger(S, Val.popcount(), Call->getType()); return true; } @@ -940,8 +962,14 @@ static bool interp__builtin_clz(InterpState &S, CodePtr OpPC, PrimType FallbackT = *S.getContext().classify(Call->getArg(1)); Fallback = popToAPSInt(S.Stk, FallbackT); } - PrimType ValT = *S.getContext().classify(Call->getArg(0)); - const APSInt &Val = popToAPSInt(S.Stk, ValT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ValT = *S.getContext().classify(Call->getArg(0)); + Val = popToAPSInt(S.Stk, ValT); + } // When the argument is 0, the result of GCC builtins is undefined, whereas // for Microsoft intrinsics, the result is the bit-width of the argument. @@ -971,8 +999,14 @@ static bool interp__builtin_ctz(InterpState &S, CodePtr OpPC, PrimType FallbackT = *S.getContext().classify(Call->getArg(1)); Fallback = popToAPSInt(S.Stk, FallbackT); } - PrimType ValT = *S.getContext().classify(Call->getArg(0)); - const APSInt &Val = popToAPSInt(S.Stk, ValT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ValT = *S.getContext().classify(Call->getArg(0)); + Val = popToAPSInt(S.Stk, ValT); + } if (Val == 0) { if (Fallback) { @@ -2505,12 +2539,8 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, } // Check if we're currently running an initializer. - for (InterpFrame *Frame = S.Current; Frame; Frame = Frame->Caller) { - if (const Function *F = Frame->getFunction(); - F && F->isConstructor() && Frame->getThis().block() == Ptr.block()) { - return Error(2); - } - } + if (llvm::is_contained(S.InitializingBlocks, Ptr.block())) + return Error(2); if (S.EvaluatingDecl && Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl) return Error(2); @@ -2518,9 +2548,9 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, return true; } -static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, - const CallExpr *Call, - unsigned BuiltinID) { +static bool interp__builtin_elementwise_int_binop( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) { assert(Call->getNumArgs() == 2); // Single integer case. @@ -2530,23 +2560,39 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, S.Stk, *S.getContext().classify(Call->getArg(1)->getType())); APSInt LHS = popToAPSInt( S.Stk, *S.getContext().classify(Call->getArg(0)->getType())); - APInt Result; - if (BuiltinID == Builtin::BI__builtin_elementwise_add_sat) { - Result = LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); - } else if (BuiltinID == Builtin::BI__builtin_elementwise_sub_sat) { - Result = LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); - } else { - llvm_unreachable("Wrong builtin ID"); - } - + APInt Result = Fn(LHS, RHS); pushInteger(S, APSInt(std::move(Result), !LHS.isSigned()), Call->getType()); return true; } + const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); + assert(VT->getElementType()->isIntegralOrEnumerationType()); + PrimType ElemT = *S.getContext().classify(VT->getElementType()); + unsigned NumElems = VT->getNumElements(); + bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); + + // Vector + Scalar case. + if (!Call->getArg(1)->getType()->isVectorType()) { + assert(Call->getArg(1)->getType()->isIntegralOrEnumerationType()); + + APSInt RHS = popToAPSInt( + S.Stk, *S.getContext().classify(Call->getArg(1)->getType())); + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + + for (unsigned I = 0; I != NumElems; ++I) { + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Dst.elem<T>(I) = static_cast<T>( + APSInt(Fn(LHS.elem<T>(I).toAPSInt(), RHS), DestUnsigned)); + }); + } + Dst.initializeAllElements(); + return true; + } + // Vector case. assert(Call->getArg(0)->getType()->isVectorType() && Call->getArg(1)->getType()->isVectorType()); - const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); assert(VT->getElementType() == Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); assert(VT->getNumElements() == @@ -2556,46 +2602,12 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, const Pointer &RHS = S.Stk.pop<Pointer>(); const Pointer &LHS = S.Stk.pop<Pointer>(); const Pointer &Dst = S.Stk.peek<Pointer>(); - PrimType ElemT = *S.getContext().classify(VT->getElementType()); - unsigned NumElems = VT->getNumElements(); for (unsigned I = 0; I != NumElems; ++I) { - APSInt Elem1; - APSInt Elem2; INT_TYPE_SWITCH_NO_BOOL(ElemT, { - Elem1 = LHS.elem<T>(I).toAPSInt(); - Elem2 = RHS.elem<T>(I).toAPSInt(); + APSInt Elem1 = LHS.elem<T>(I).toAPSInt(); + APSInt Elem2 = RHS.elem<T>(I).toAPSInt(); + Dst.elem<T>(I) = static_cast<T>(APSInt(Fn(Elem1, Elem2), DestUnsigned)); }); - - APSInt Result; - switch (BuiltinID) { - case Builtin::BI__builtin_elementwise_add_sat: - Result = APSInt(Elem1.isSigned() ? Elem1.sadd_sat(Elem2) - : Elem1.uadd_sat(Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - break; - case Builtin::BI__builtin_elementwise_sub_sat: - Result = APSInt(Elem1.isSigned() ? Elem1.ssub_sat(Elem2) - : Elem1.usub_sat(Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - break; - case clang::X86::BI__builtin_ia32_pmulhuw128: - case clang::X86::BI__builtin_ia32_pmulhuw256: - case clang::X86::BI__builtin_ia32_pmulhuw512: - Result = APSInt(llvm::APIntOps::mulhu(Elem1, Elem2), - /*isUnsigned=*/true); - break; - case clang::X86::BI__builtin_ia32_pmulhw128: - case clang::X86::BI__builtin_ia32_pmulhw256: - case clang::X86::BI__builtin_ia32_pmulhw512: - Result = APSInt(llvm::APIntOps::mulhs(Elem1, Elem2), - /*isUnsigned=*/false); - break; - default: - llvm_unreachable("Wrong builtin ID"); - } - - INT_TYPE_SWITCH_NO_BOOL(ElemT, - { Dst.elem<T>(I) = static_cast<T>(Result); }); } Dst.initializeAllElements(); @@ -2724,8 +2736,11 @@ static bool interp__builtin_ia32_pmul(InterpState &S, CodePtr OpPC, return true; } -static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, - const CallExpr *Call) { +static bool interp__builtin_elementwise_triop_fp( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APFloat(const APFloat &, const APFloat &, + const APFloat &, llvm::RoundingMode)> + Fn) { assert(Call->getNumArgs() == 3); FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); @@ -2744,8 +2759,7 @@ static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, const Floating &Z = S.Stk.pop<Floating>(); const Floating &Y = S.Stk.pop<Floating>(); const Floating &X = S.Stk.pop<Floating>(); - APFloat F = X.getAPFloat(); - F.fusedMultiplyAdd(Y.getAPFloat(), Z.getAPFloat(), RM); + APFloat F = Fn(X.getAPFloat(), Y.getAPFloat(), Z.getAPFloat(), RM); Floating Result = S.allocFloat(X.getSemantics()); Result.copy(F); S.Stk.push<Floating>(Result); @@ -2776,8 +2790,8 @@ static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, APFloat X = VX.elem<T>(I).getAPFloat(); APFloat Y = VY.elem<T>(I).getAPFloat(); APFloat Z = VZ.elem<T>(I).getAPFloat(); - (void)X.fusedMultiplyAdd(Y, Z, RM); - Dst.elem<Floating>(I) = Floating(X); + APFloat F = Fn(X, Y, Z, RM); + Dst.elem<Floating>(I) = Floating(F); } Dst.initializeAllElements(); return true; @@ -2817,6 +2831,72 @@ static bool interp__builtin_select(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_elementwise_triop( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &)> + Fn) { + assert(Call->getNumArgs() == 3); + + QualType Arg0Type = Call->getArg(0)->getType(); + QualType Arg1Type = Call->getArg(1)->getType(); + QualType Arg2Type = Call->getArg(2)->getType(); + + // Non-vector integer types. + if (!Arg0Type->isVectorType()) { + const APSInt &Op2 = popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type)); + const APSInt &Op1 = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type)); + const APSInt &Op0 = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type)); + APSInt Result = APSInt(Fn(Op0, Op1, Op2), Op0.isUnsigned()); + pushInteger(S, Result, Call->getType()); + return true; + } + + const auto *VecT = Arg0Type->castAs<VectorType>(); + const PrimType &ElemT = *S.getContext().classify(VecT->getElementType()); + unsigned NumElems = VecT->getNumElements(); + bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); + + // Vector + Vector + Scalar case. + if (!Arg2Type->isVectorType()) { + APSInt Op2 = popToAPSInt( + S.Stk, *S.getContext().classify(Call->getArg(2)->getType())); + + const Pointer &Op1 = S.Stk.pop<Pointer>(); + const Pointer &Op0 = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + for (unsigned I = 0; I != NumElems; ++I) { + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Dst.elem<T>(I) = static_cast<T>(APSInt( + Fn(Op0.elem<T>(I).toAPSInt(), Op1.elem<T>(I).toAPSInt(), Op2), + DestUnsigned)); + }); + } + Dst.initializeAllElements(); + + return true; + } + + // Vector type. + const Pointer &Op2 = S.Stk.pop<Pointer>(); + const Pointer &Op1 = S.Stk.pop<Pointer>(); + const Pointer &Op0 = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + for (unsigned I = 0; I != NumElems; ++I) { + APSInt Val0, Val1, Val2; + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Val0 = Op0.elem<T>(I).toAPSInt(); + Val1 = Op1.elem<T>(I).toAPSInt(); + Val2 = Op2.elem<T>(I).toAPSInt(); + }); + APSInt Result = APSInt(Fn(Val0, Val1, Val2), Val0.isUnsigned()); + INT_TYPE_SWITCH_NO_BOOL(ElemT, + { Dst.elem<T>(I) = static_cast<T>(Result); }); + } + Dst.initializeAllElements(); + + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, uint32_t BuiltinID) { if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID)) @@ -3229,14 +3309,139 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, return interp__builtin_is_within_lifetime(S, OpPC, Call); case Builtin::BI__builtin_elementwise_add_sat: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); + }); + case Builtin::BI__builtin_elementwise_sub_sat: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); + }); + + case clang::X86::BI__builtin_ia32_pavgb128: + case clang::X86::BI__builtin_ia32_pavgw128: + case clang::X86::BI__builtin_ia32_pavgb256: + case clang::X86::BI__builtin_ia32_pavgw256: + case clang::X86::BI__builtin_ia32_pavgb512: + case clang::X86::BI__builtin_ia32_pavgw512: + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::avgCeilU); + case clang::X86::BI__builtin_ia32_pmulhuw128: case clang::X86::BI__builtin_ia32_pmulhuw256: case clang::X86::BI__builtin_ia32_pmulhuw512: + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::mulhu); + case clang::X86::BI__builtin_ia32_pmulhw128: case clang::X86::BI__builtin_ia32_pmulhw256: case clang::X86::BI__builtin_ia32_pmulhw512: - return interp__builtin_elementwise_sat(S, OpPC, Call, BuiltinID); + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::mulhs); + + case clang::X86::BI__builtin_ia32_psllv2di: + case clang::X86::BI__builtin_ia32_psllv4di: + case clang::X86::BI__builtin_ia32_psllv4si: + case clang::X86::BI__builtin_ia32_psllv8di: + case clang::X86::BI__builtin_ia32_psllv8hi: + case clang::X86::BI__builtin_ia32_psllv8si: + case clang::X86::BI__builtin_ia32_psllv16hi: + case clang::X86::BI__builtin_ia32_psllv16si: + case clang::X86::BI__builtin_ia32_psllv32hi: + case clang::X86::BI__builtin_ia32_psllwi128: + case clang::X86::BI__builtin_ia32_psllwi256: + case clang::X86::BI__builtin_ia32_psllwi512: + case clang::X86::BI__builtin_ia32_pslldi128: + case clang::X86::BI__builtin_ia32_pslldi256: + case clang::X86::BI__builtin_ia32_pslldi512: + case clang::X86::BI__builtin_ia32_psllqi128: + case clang::X86::BI__builtin_ia32_psllqi256: + case clang::X86::BI__builtin_ia32_psllqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); + } + return LHS.shl(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_psrav4si: + case clang::X86::BI__builtin_ia32_psrav8di: + case clang::X86::BI__builtin_ia32_psrav8hi: + case clang::X86::BI__builtin_ia32_psrav8si: + case clang::X86::BI__builtin_ia32_psrav16hi: + case clang::X86::BI__builtin_ia32_psrav16si: + case clang::X86::BI__builtin_ia32_psrav32hi: + case clang::X86::BI__builtin_ia32_psravq128: + case clang::X86::BI__builtin_ia32_psravq256: + case clang::X86::BI__builtin_ia32_psrawi128: + case clang::X86::BI__builtin_ia32_psrawi256: + case clang::X86::BI__builtin_ia32_psrawi512: + case clang::X86::BI__builtin_ia32_psradi128: + case clang::X86::BI__builtin_ia32_psradi256: + case clang::X86::BI__builtin_ia32_psradi512: + case clang::X86::BI__builtin_ia32_psraqi128: + case clang::X86::BI__builtin_ia32_psraqi256: + case clang::X86::BI__builtin_ia32_psraqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return LHS.ashr(LHS.getBitWidth() - 1); + } + return LHS.ashr(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_psrlv2di: + case clang::X86::BI__builtin_ia32_psrlv4di: + case clang::X86::BI__builtin_ia32_psrlv4si: + case clang::X86::BI__builtin_ia32_psrlv8di: + case clang::X86::BI__builtin_ia32_psrlv8hi: + case clang::X86::BI__builtin_ia32_psrlv8si: + case clang::X86::BI__builtin_ia32_psrlv16hi: + case clang::X86::BI__builtin_ia32_psrlv16si: + case clang::X86::BI__builtin_ia32_psrlv32hi: + case clang::X86::BI__builtin_ia32_psrlwi128: + case clang::X86::BI__builtin_ia32_psrlwi256: + case clang::X86::BI__builtin_ia32_psrlwi512: + case clang::X86::BI__builtin_ia32_psrldi128: + case clang::X86::BI__builtin_ia32_psrldi256: + case clang::X86::BI__builtin_ia32_psrldi512: + case clang::X86::BI__builtin_ia32_psrlqi128: + case clang::X86::BI__builtin_ia32_psrlqi256: + case clang::X86::BI__builtin_ia32_psrlqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); + } + return LHS.lshr(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_vprotbi: + case clang::X86::BI__builtin_ia32_vprotdi: + case clang::X86::BI__builtin_ia32_vprotqi: + case clang::X86::BI__builtin_ia32_vprotwi: + case clang::X86::BI__builtin_ia32_prold128: + case clang::X86::BI__builtin_ia32_prold256: + case clang::X86::BI__builtin_ia32_prold512: + case clang::X86::BI__builtin_ia32_prolq128: + case clang::X86::BI__builtin_ia32_prolq256: + case clang::X86::BI__builtin_ia32_prolq512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotl(RHS); }); + + case clang::X86::BI__builtin_ia32_prord128: + case clang::X86::BI__builtin_ia32_prord256: + case clang::X86::BI__builtin_ia32_prord512: + case clang::X86::BI__builtin_ia32_prorq128: + case clang::X86::BI__builtin_ia32_prorq256: + case clang::X86::BI__builtin_ia32_prorq512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); }); case Builtin::BI__builtin_elementwise_max: case Builtin::BI__builtin_elementwise_min: @@ -3251,7 +3456,61 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, return interp__builtin_ia32_pmul(S, OpPC, Call, BuiltinID); case Builtin::BI__builtin_elementwise_fma: - return interp__builtin_elementwise_fma(S, OpPC, Call); + return interp__builtin_elementwise_triop_fp( + S, OpPC, Call, + [](const APFloat &X, const APFloat &Y, const APFloat &Z, + llvm::RoundingMode RM) { + APFloat F = X; + F.fusedMultiplyAdd(Y, Z, RM); + return F; + }); + + case X86::BI__builtin_ia32_vpshldd128: + case X86::BI__builtin_ia32_vpshldd256: + case X86::BI__builtin_ia32_vpshldd512: + case X86::BI__builtin_ia32_vpshldq128: + case X86::BI__builtin_ia32_vpshldq256: + case X86::BI__builtin_ia32_vpshldq512: + case X86::BI__builtin_ia32_vpshldw128: + case X86::BI__builtin_ia32_vpshldw256: + case X86::BI__builtin_ia32_vpshldw512: + return interp__builtin_elementwise_triop( + S, OpPC, Call, + [](const APSInt &Hi, const APSInt &Lo, const APSInt &Amt) { + return llvm::APIntOps::fshl(Hi, Lo, Amt); + }); + + case X86::BI__builtin_ia32_vpshrdd128: + case X86::BI__builtin_ia32_vpshrdd256: + case X86::BI__builtin_ia32_vpshrdd512: + case X86::BI__builtin_ia32_vpshrdq128: + case X86::BI__builtin_ia32_vpshrdq256: + case X86::BI__builtin_ia32_vpshrdq512: + case X86::BI__builtin_ia32_vpshrdw128: + case X86::BI__builtin_ia32_vpshrdw256: + case X86::BI__builtin_ia32_vpshrdw512: + // NOTE: Reversed Hi/Lo operands. + return interp__builtin_elementwise_triop( + S, OpPC, Call, + [](const APSInt &Lo, const APSInt &Hi, const APSInt &Amt) { + return llvm::APIntOps::fshr(Hi, Lo, Amt); + }); + + case clang::X86::BI__builtin_ia32_blendvpd: + case clang::X86::BI__builtin_ia32_blendvpd256: + case clang::X86::BI__builtin_ia32_blendvps: + case clang::X86::BI__builtin_ia32_blendvps256: + return interp__builtin_elementwise_triop_fp( + S, OpPC, Call, + [](const APFloat &F, const APFloat &T, const APFloat &C, + llvm::RoundingMode) { return C.isNegative() ? T : F; }); + + case clang::X86::BI__builtin_ia32_pblendvb128: + case clang::X86::BI__builtin_ia32_pblendvb256: + return interp__builtin_elementwise_triop( + S, OpPC, Call, [](const APSInt &F, const APSInt &T, const APSInt &C) { + return ((APInt)C).isNegative() ? T : F; + }); case X86::BI__builtin_ia32_selectb_128: case X86::BI__builtin_ia32_selectb_256: @@ -3279,6 +3538,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case X86::BI__builtin_ia32_selectpd_512: return interp__builtin_select(S, OpPC, Call); + case Builtin::BI__builtin_elementwise_fshl: + return interp__builtin_elementwise_triop(S, OpPC, Call, + llvm::APIntOps::fshl); + case Builtin::BI__builtin_elementwise_fshr: + return interp__builtin_elementwise_triop(S, OpPC, Call, + llvm::APIntOps::fshr); + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) @@ -3303,11 +3569,8 @@ bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, switch (Node.getKind()) { case OffsetOfNode::Field: { const FieldDecl *MemberDecl = Node.getField(); - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) - return false; - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - if (RD->isInvalidDecl()) + const auto *RD = CurrentType->getAsRecordDecl(); + if (!RD || RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); unsigned FieldIndex = MemberDecl->getFieldIndex(); @@ -3336,23 +3599,19 @@ bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, return false; // Find the layout of the class whose base we are looking into. - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) - return false; - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - if (RD->isInvalidDecl()) + const auto *RD = CurrentType->getAsCXXRecordDecl(); + if (!RD || RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); // Find the base class itself. CurrentType = BaseSpec->getType(); - const RecordType *BaseRT = CurrentType->getAs<RecordType>(); - if (!BaseRT) + const auto *BaseRD = CurrentType->getAsCXXRecordDecl(); + if (!BaseRD) return false; // Add the offset to the base. - Result += RL.getBaseClassOffset(cast<CXXRecordDecl>( - BaseRT->getOriginalDecl()->getDefinitionOrSelf())); + Result += RL.getBaseClassOffset(BaseRD); break; } case OffsetOfNode::Identifier: diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp index 18400b10c47b..b9dc2aed2311 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.cpp +++ b/clang/lib/AST/ByteCode/InterpFrame.cpp @@ -195,12 +195,6 @@ void InterpFrame::describe(llvm::raw_ostream &OS) const { OS << ")"; } -Frame *InterpFrame::getCaller() const { - if (Caller->Caller) - return Caller; - return S.getSplitFrame(); -} - SourceRange InterpFrame::getCallRange() const { if (!Caller->Func) { if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid()) diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h index 4be53911b615..cf4d27d341e9 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.h +++ b/clang/lib/AST/ByteCode/InterpFrame.h @@ -59,7 +59,7 @@ public: void describe(llvm::raw_ostream &OS) const override; /// Returns the parent frame object. - Frame *getCaller() const override; + Frame *getCaller() const override { return Caller; } /// Returns the location of the call to the frame. SourceRange getCallRange() const override; diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index f89967759ff9..1ec4191d2ba3 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -20,19 +20,29 @@ using namespace clang::interp; InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), - Current(&BottomFrame) {} + Current(&BottomFrame) { + InConstantContext = Parent.InConstantContext; + CheckingPotentialConstantExpression = + Parent.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; +} InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, const Function *Func) : Parent(Parent), M(nullptr), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()), - Current(&BottomFrame) {} + Current(&BottomFrame) { + InConstantContext = Parent.InConstantContext; + CheckingPotentialConstantExpression = + Parent.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; +} bool InterpState::inConstantContext() const { if (ConstantContextOverride) return *ConstantContextOverride; - return Parent.InConstantContext; + return InConstantContext; } InterpState::~InterpState() { @@ -59,20 +69,11 @@ InterpState::~InterpState() { void InterpState::cleanup() { // As a last resort, make sure all pointers still pointing to a dead block // don't point to it anymore. - Alloc.cleanup(); -} - -Frame *InterpState::getCurrentFrame() { - if (Current && Current->Caller) - return Current; - return Parent.getCurrentFrame(); + if (Alloc) + Alloc->cleanup(); } -bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) { - QualType Type = E->getType(); - CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; - return noteUndefinedBehavior(); -} +Frame *InterpState::getCurrentFrame() { return Current; } void InterpState::deallocate(Block *B) { assert(B); @@ -103,10 +104,13 @@ void InterpState::deallocate(Block *B) { } bool InterpState::maybeDiagnoseDanglingAllocations() { - bool NoAllocationsLeft = !Alloc.hasAllocations(); + if (!Alloc) + return true; + + bool NoAllocationsLeft = !Alloc->hasAllocations(); if (!checkingPotentialConstantExpression()) { - for (const auto &[Source, Site] : Alloc.allocation_sites()) { + for (const auto &[Source, Site] : Alloc->allocation_sites()) { assert(!Site.empty()); CCEDiag(Source->getExprLoc(), diag::note_constexpr_memory_leak) diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index 861e4c38049a..e095908bce98 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -57,14 +57,11 @@ public: bool diagnosing() const { return getEvalStatus().Diag != nullptr; } // Stack frame accessors. - Frame *getSplitFrame() { return Parent.getCurrentFrame(); } Frame *getCurrentFrame() override; unsigned getCallStackDepth() override { return Current ? (Current->getDepth() + 1) : 1; } - const Frame *getBottomFrame() const override { - return Parent.getBottomFrame(); - } + const Frame *getBottomFrame() const override { return &BottomFrame; } // Access objects from the walker context. Expr::EvalStatus &getEvalStatus() const override { @@ -73,18 +70,12 @@ public: ASTContext &getASTContext() const override { return Parent.getASTContext(); } // Forward status checks and updates to the walker. - bool checkingForUndefinedBehavior() const override { - return Parent.checkingForUndefinedBehavior(); - } bool keepEvaluatingAfterFailure() const override { return Parent.keepEvaluatingAfterFailure(); } bool keepEvaluatingAfterSideEffect() const override { return Parent.keepEvaluatingAfterSideEffect(); } - bool checkingPotentialConstantExpression() const override { - return Parent.checkingPotentialConstantExpression(); - } bool noteUndefinedBehavior() override { return Parent.noteUndefinedBehavior(); } @@ -99,9 +90,6 @@ public: bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); } bool noteSideEffect() override { return Parent.noteSideEffect(); } - /// Reports overflow and return true if evaluation should continue. - bool reportOverflow(const Expr *E, const llvm::APSInt &Value); - /// Deallocates a pointer. void deallocate(Block *B); @@ -118,7 +106,13 @@ public: void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; } - DynamicAllocator &getAllocator() { return Alloc; } + DynamicAllocator &getAllocator() { + if (!Alloc) { + Alloc = std::make_unique<DynamicAllocator>(); + } + + return *Alloc.get(); + } /// Diagnose any dynamic allocations that haven't been freed yet. /// Will return \c false if there were any allocations to diagnose, @@ -164,7 +158,7 @@ private: /// Reference to the offset-source mapping. SourceMapper *M; /// Allocator used for dynamic allocations performed via the program. - DynamicAllocator Alloc; + std::unique_ptr<DynamicAllocator> Alloc; public: /// Reference to the module containing all bytecode. diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 38856ad256a6..ef75b0ded4f1 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -442,26 +442,42 @@ bool Pointer::isInitialized() const { assert(BS.Pointee && "Cannot check if null pointer was initialized"); const Descriptor *Desc = getFieldDesc(); assert(Desc); - if (Desc->isPrimitiveArray()) { - if (isStatic() && BS.Base == 0) - return true; + if (Desc->isPrimitiveArray()) + return isElementInitialized(getIndex()); - InitMapPtr &IM = getInitMap(); + if (asBlockPointer().Base == 0) + return true; + // Field has its bit in an inline descriptor. + return getInlineDesc()->IsInitialized; +} + +bool Pointer::isElementInitialized(unsigned Index) const { + if (!isBlockPointer()) + return true; + const Descriptor *Desc = getFieldDesc(); + assert(Desc); + + if (isStatic() && BS.Base == 0) + return true; + + if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor)) { + const GlobalInlineDescriptor &GD = + *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); + return GD.InitState == GlobalInitState::Initialized; + } + + if (Desc->isPrimitiveArray()) { + InitMapPtr &IM = getInitMap(); if (!IM) return false; if (IM->first) return true; - return IM->second->isElementInitialized(getIndex()); + return IM->second->isElementInitialized(Index); } - - if (asBlockPointer().Base == 0) - return true; - - // Field has its bit in an inline descriptor. - return getInlineDesc()->IsInitialized; + return isInitialized(); } void Pointer::initialize() const { @@ -524,6 +540,23 @@ void Pointer::initializeAllElements() const { } } +bool Pointer::allElementsInitialized() const { + assert(getFieldDesc()->isPrimitiveArray()); + assert(isArrayRoot()); + + if (isStatic() && BS.Base == 0) + return true; + + if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor)) { + const GlobalInlineDescriptor &GD = + *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); + return GD.InitState == GlobalInitState::Initialized; + } + + InitMapPtr &IM = getInitMap(); + return IM && IM->first; +} + void Pointer::activate() const { // Field has its bit in an inline descriptor. assert(BS.Base != 0 && "Only composite fields can be activated"); @@ -594,9 +627,6 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { if (A.isTypeidPointer() && B.isTypeidPointer()) return true; - if (A.isIntegralPointer() || B.isIntegralPointer()) - return A.getSource() == B.getSource(); - if (A.StorageKind != B.StorageKind) return false; @@ -700,7 +730,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, return true; } - if (const auto *RT = Ty->getAs<RecordType>()) { + if (const auto *RT = Ty->getAsCanonical<RecordType>()) { const auto *Record = Ptr.getRecord(); assert(Record && "Missing record descriptor"); @@ -771,13 +801,13 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, R = APValue(APValue::UninitArray{}, NumElems, NumElems); bool Ok = true; - for (unsigned I = 0; I < NumElems; ++I) { + OptPrimType ElemT = Ctx.classify(ElemTy); + for (unsigned I = 0; I != NumElems; ++I) { APValue &Slot = R.getArrayInitializedElt(I); - const Pointer &EP = Ptr.atIndex(I); - if (OptPrimType T = Ctx.classify(ElemTy)) { - TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx)); + if (ElemT) { + TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx)); } else { - Ok &= Composite(ElemTy, EP.narrow(), Slot); + Ok &= Composite(ElemTy, Ptr.atIndex(I).narrow(), Slot); } } return Ok; @@ -785,8 +815,11 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, // Complex types. if (const auto *CT = Ty->getAs<ComplexType>()) { - QualType ElemTy = CT->getElementType(); + // Can happen via C casts. + if (!Ptr.getFieldDesc()->isPrimitiveArray()) + return false; + QualType ElemTy = CT->getElementType(); if (ElemTy->isIntegerType()) { OptPrimType ElemT = Ctx.classify(ElemTy); assert(ElemT); diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 0ce54ab0a17d..49d701c3e27b 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -103,9 +103,7 @@ public: Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0) : Offset(Offset), StorageKind(Storage::Int), Int{Desc, Address} {} Pointer(const Function *F, uint64_t Offset = 0) - : Offset(Offset), StorageKind(Storage::Fn), Fn(F) { - Fn = FunctionPointer(F); - } + : Offset(Offset), StorageKind(Storage::Fn), Fn(F) {} Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0) : Offset(Offset), StorageKind(Storage::Typeid) { Typeid.TypePtr = TypePtr; @@ -122,17 +120,14 @@ public: if (P.StorageKind != StorageKind) return false; if (isIntegralPointer()) - return P.asIntPointer().Value == asIntPointer().Value && - P.asIntPointer().Desc == asIntPointer().Desc && P.Offset == Offset; + return P.Int.Value == Int.Value && P.Int.Desc == Int.Desc && + P.Offset == Offset; if (isFunctionPointer()) - return P.asFunctionPointer().getFunction() == - asFunctionPointer().getFunction() && - P.Offset == Offset; + return P.Fn.getFunction() == Fn.getFunction() && P.Offset == Offset; assert(isBlockPointer()); - return P.asBlockPointer().Pointee == asBlockPointer().Pointee && - P.asBlockPointer().Base == asBlockPointer().Base && + return P.BS.Pointee == BS.Pointee && P.BS.Base == BS.Base && P.Offset == Offset; } @@ -146,10 +141,10 @@ public: uint64_t getIntegerRepresentation() const { if (isIntegralPointer()) - return asIntPointer().Value + (Offset * elemSize()); + return Int.Value + (Offset * elemSize()); if (isFunctionPointer()) - return asFunctionPointer().getIntegerRepresentation() + Offset; - return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset; + return Fn.getIntegerRepresentation() + Offset; + return reinterpret_cast<uint64_t>(BS.Pointee) + Offset; } /// Converts the pointer to an APValue that is an rvalue. @@ -159,27 +154,25 @@ public: /// Offsets a pointer inside an array. [[nodiscard]] Pointer atIndex(uint64_t Idx) const { if (isIntegralPointer()) - return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx); + return Pointer(Int.Value, Int.Desc, Idx); if (isFunctionPointer()) - return Pointer(asFunctionPointer().getFunction(), Idx); + return Pointer(Fn.getFunction(), Idx); - if (asBlockPointer().Base == RootPtrMark) - return Pointer(asBlockPointer().Pointee, RootPtrMark, - getDeclDesc()->getSize()); + if (BS.Base == RootPtrMark) + return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize()); uint64_t Off = Idx * elemSize(); if (getFieldDesc()->ElemDesc) Off += sizeof(InlineDescriptor); else Off += sizeof(InitMapPtr); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, - asBlockPointer().Base + Off); + return Pointer(BS.Pointee, BS.Base, BS.Base + Off); } /// Creates a pointer to a field. [[nodiscard]] Pointer atField(unsigned Off) const { assert(isBlockPointer()); unsigned Field = Offset + Off; - return Pointer(asBlockPointer().Pointee, Field, Field); + return Pointer(BS.Pointee, Field, Field); } /// Subtract the given offset from the current Base and Offset @@ -187,7 +180,7 @@ public: [[nodiscard]] Pointer atFieldSub(unsigned Off) const { assert(Offset >= Off); unsigned O = Offset - Off; - return Pointer(asBlockPointer().Pointee, O, O); + return Pointer(BS.Pointee, O, O); } /// Restricts the scope of an array element pointer. @@ -199,15 +192,15 @@ public: if (isZero() || isUnknownSizeArray()) return *this; - unsigned Base = asBlockPointer().Base; + unsigned Base = BS.Base; // Pointer to an array of base types - enter block. if (Base == RootPtrMark) - return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor), + return Pointer(BS.Pointee, sizeof(InlineDescriptor), Offset == 0 ? Offset : PastEndMark); // Pointer is one past end - magic offset marks that. if (isOnePastEnd()) - return Pointer(asBlockPointer().Pointee, Base, PastEndMark); + return Pointer(BS.Pointee, Base, PastEndMark); if (Offset != Base) { // If we're pointing to a primitive array element, there's nothing to do. @@ -215,7 +208,7 @@ public: return *this; // Pointer is to a composite array element - enter it. if (Offset != Base) - return Pointer(asBlockPointer().Pointee, Offset, Offset); + return Pointer(BS.Pointee, Offset, Offset); } // Otherwise, we're pointing to a non-array element or @@ -226,7 +219,7 @@ public: /// Expands a pointer to the containing array, undoing narrowing. [[nodiscard]] Pointer expand() const { assert(isBlockPointer()); - Block *Pointee = asBlockPointer().Pointee; + Block *Pointee = BS.Pointee; if (isElementPastEnd()) { // Revert to an outer one-past-end pointer. @@ -235,19 +228,18 @@ public: Adjust = sizeof(InitMapPtr); else Adjust = sizeof(InlineDescriptor); - return Pointer(Pointee, asBlockPointer().Base, - asBlockPointer().Base + getSize() + Adjust); + return Pointer(Pointee, BS.Base, BS.Base + getSize() + Adjust); } // Do not step out of array elements. - if (asBlockPointer().Base != Offset) + if (BS.Base != Offset) return *this; if (isRoot()) - return Pointer(Pointee, asBlockPointer().Base, asBlockPointer().Base); + return Pointer(Pointee, BS.Base, BS.Base); // Step into the containing array, if inside one. - unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset; + unsigned Next = BS.Base - getInlineDesc()->Offset; const Descriptor *Desc = (Next == Pointee->getDescriptor()->getMetadataSize()) ? getDeclDesc() @@ -260,19 +252,19 @@ public: /// Checks if the pointer is null. bool isZero() const { if (isBlockPointer()) - return asBlockPointer().Pointee == nullptr; + return BS.Pointee == nullptr; if (isFunctionPointer()) - return asFunctionPointer().isZero(); + return Fn.isZero(); if (isTypeidPointer()) return false; assert(isIntegralPointer()); - return asIntPointer().Value == 0 && Offset == 0; + return Int.Value == 0 && Offset == 0; } /// Checks if the pointer is live. bool isLive() const { if (!isBlockPointer()) return true; - return asBlockPointer().Pointee && !asBlockPointer().Pointee->isDead(); + return BS.Pointee && !BS.Pointee->isDead(); } /// Checks if the item is a field in an object. bool isField() const { @@ -285,13 +277,13 @@ public: /// Accessor for information about the declaration site. const Descriptor *getDeclDesc() const { if (isIntegralPointer()) - return asIntPointer().Desc; + return Int.Desc; if (isFunctionPointer() || isTypeidPointer()) return nullptr; assert(isBlockPointer()); - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->Desc; + assert(BS.Pointee); + return BS.Pointee->Desc; } SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } @@ -300,37 +292,36 @@ public: if (isBlockPointer()) return getDeclDesc()->getSource(); if (isFunctionPointer()) { - const Function *F = asFunctionPointer().getFunction(); + const Function *F = Fn.getFunction(); return F ? F->getDecl() : DeclTy(); } assert(isIntegralPointer()); - return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy(); + return Int.Desc ? Int.Desc->getSource() : DeclTy(); } /// Returns a pointer to the object of which this pointer is a field. [[nodiscard]] Pointer getBase() const { - if (asBlockPointer().Base == RootPtrMark) { + if (BS.Base == RootPtrMark) { assert(Offset == PastEndMark && "cannot get base of a block"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); + return Pointer(BS.Pointee, BS.Base, 0); } - unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset; - return Pointer(asBlockPointer().Pointee, NewBase, NewBase); + unsigned NewBase = BS.Base - getInlineDesc()->Offset; + return Pointer(BS.Pointee, NewBase, NewBase); } /// Returns the parent array. [[nodiscard]] Pointer getArray() const { - if (asBlockPointer().Base == RootPtrMark) { + if (BS.Base == RootPtrMark) { assert(Offset != 0 && Offset != PastEndMark && "not an array element"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); + return Pointer(BS.Pointee, BS.Base, 0); } - assert(Offset != asBlockPointer().Base && "not an array element"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, - asBlockPointer().Base); + assert(Offset != BS.Base && "not an array element"); + return Pointer(BS.Pointee, BS.Base, BS.Base); } /// Accessors for information about the innermost field. const Descriptor *getFieldDesc() const { if (isIntegralPointer()) - return asIntPointer().Desc; + return Int.Desc; if (isRoot()) return getDeclDesc(); @@ -342,9 +333,9 @@ public: if (isTypeidPointer()) return QualType(Typeid.TypeInfoType, 0); if (isFunctionPointer()) - return asFunctionPointer().getFunction()->getDecl()->getType(); + return Fn.getFunction()->getDecl()->getType(); - if (inPrimitiveArray() && Offset != asBlockPointer().Base) { + if (inPrimitiveArray() && Offset != BS.Base) { // Unfortunately, complex and vector types are not array types in clang, // but they are for us. if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe()) @@ -357,19 +348,17 @@ public: return getFieldDesc()->getType(); } - [[nodiscard]] Pointer getDeclPtr() const { - return Pointer(asBlockPointer().Pointee); - } + [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); } /// Returns the element size of the innermost field. size_t elemSize() const { if (isIntegralPointer()) { - if (!asIntPointer().Desc) + if (!Int.Desc) return 1; - return asIntPointer().Desc->getElemSize(); + return Int.Desc->getElemSize(); } - if (asBlockPointer().Base == RootPtrMark) + if (BS.Base == RootPtrMark) return getDeclDesc()->getSize(); return getFieldDesc()->getElemSize(); } @@ -383,24 +372,22 @@ public: unsigned getOffset() const { assert(Offset != PastEndMark && "invalid offset"); assert(isBlockPointer()); - if (asBlockPointer().Base == RootPtrMark) + if (BS.Base == RootPtrMark) return Offset; unsigned Adjust = 0; - if (Offset != asBlockPointer().Base) { + if (Offset != BS.Base) { if (getFieldDesc()->ElemDesc) Adjust = sizeof(InlineDescriptor); else Adjust = sizeof(InitMapPtr); } - return Offset - asBlockPointer().Base - Adjust; + return Offset - BS.Base - Adjust; } /// Whether this array refers to an array, but not /// to the first element. - bool isArrayRoot() const { - return inArray() && Offset == asBlockPointer().Base; - } + bool isArrayRoot() const { return inArray() && Offset == BS.Base; } /// Checks if the innermost field is an array. bool inArray() const { @@ -409,7 +396,7 @@ public: return false; } bool inUnion() const { - if (isBlockPointer() && asBlockPointer().Base >= sizeof(InlineDescriptor)) + if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor)) return getInlineDesc()->InUnion; return false; }; @@ -431,7 +418,7 @@ public: if (!isBlockPointer()) return false; - const BlockPointer &BP = asBlockPointer(); + const BlockPointer &BP = BS; if (inArray() && BP.Base != Offset) return true; @@ -446,16 +433,15 @@ public: bool isRoot() const { if (isZero() || !isBlockPointer()) return true; - return (asBlockPointer().Base == - asBlockPointer().Pointee->getDescriptor()->getMetadataSize() || - asBlockPointer().Base == 0); + return (BS.Base == BS.Pointee->getDescriptor()->getMetadataSize() || + BS.Base == 0); } /// If this pointer has an InlineDescriptor we can use to initialize. bool canBeInitialized() const { if (!isBlockPointer()) return false; - return asBlockPointer().Pointee && asBlockPointer().Base > 0; + return BS.Pointee && BS.Base > 0; } [[nodiscard]] const BlockPointer &asBlockPointer() const { @@ -497,29 +483,29 @@ public: /// Checks if the storage is extern. bool isExtern() const { if (isBlockPointer()) - return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern(); + return BS.Pointee && BS.Pointee->isExtern(); return false; } /// Checks if the storage is static. bool isStatic() const { if (!isBlockPointer()) return true; - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isStatic(); + assert(BS.Pointee); + return BS.Pointee->isStatic(); } /// Checks if the storage is temporary. bool isTemporary() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isTemporary(); + assert(BS.Pointee); + return BS.Pointee->isTemporary(); } return false; } /// Checks if the storage has been dynamically allocated. bool isDynamic() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isDynamic(); + assert(BS.Pointee); + return BS.Pointee->isDynamic(); } return false; } @@ -535,15 +521,13 @@ public: bool isWeak() const { if (isFunctionPointer()) - return asFunctionPointer().isWeak(); + return Fn.isWeak(); if (!isBlockPointer()) return false; assert(isBlockPointer()); - return asBlockPointer().Pointee->isWeak(); + return BS.Pointee->isWeak(); } - /// Checks if an object was initialized. - bool isInitialized() const; /// Checks if the object is active. bool isActive() const { if (!isBlockPointer()) @@ -560,7 +544,7 @@ public: if (!isBlockPointer()) return false; - if (const Block *Pointee = asBlockPointer().Pointee) + if (const Block *Pointee = BS.Pointee) return Pointee->isDummy(); return false; } @@ -587,8 +571,8 @@ public: /// Returns the declaration ID. UnsignedOrNone getDeclID() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->getDeclID(); + assert(BS.Pointee); + return BS.Pointee->getDeclID(); } return std::nullopt; } @@ -596,9 +580,9 @@ public: /// Returns the byte offset from the start. uint64_t getByteOffset() const { if (isIntegralPointer()) - return asIntPointer().Value + Offset; + return Int.Value + Offset; if (isTypeidPointer()) - return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset; + return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset; if (isOnePastEnd()) return PastEndMark; return Offset; @@ -611,13 +595,13 @@ public: return getSize() / elemSize(); } - const Block *block() const { return asBlockPointer().Pointee; } + const Block *block() const { return BS.Pointee; } /// If backed by actual data (i.e. a block pointer), return /// an address to that data. const std::byte *getRawAddress() const { assert(isBlockPointer()); - return asBlockPointer().Pointee->rawData() + Offset; + return BS.Pointee->rawData() + Offset; } /// Returns the index into an array. @@ -629,8 +613,7 @@ public: return 0; // narrow()ed element in a composite array. - if (asBlockPointer().Base > sizeof(InlineDescriptor) && - asBlockPointer().Base == Offset) + if (BS.Base > sizeof(InlineDescriptor) && BS.Base == Offset) return 0; if (auto ElemSize = elemSize()) @@ -643,7 +626,7 @@ public: if (!isBlockPointer()) return false; - if (!asBlockPointer().Pointee) + if (!BS.Pointee) return false; if (isUnknownSizeArray()) @@ -676,16 +659,15 @@ public: template <typename T> T &deref() const { assert(isLive() && "Invalid pointer"); assert(isBlockPointer()); - assert(asBlockPointer().Pointee); + assert(BS.Pointee); assert(isDereferencable()); - assert(Offset + sizeof(T) <= - asBlockPointer().Pointee->getDescriptor()->getAllocSize()); + assert(Offset + sizeof(T) <= BS.Pointee->getDescriptor()->getAllocSize()); if (isArrayRoot()) - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base + sizeof(InitMapPtr)); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.Base + + sizeof(InitMapPtr)); - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + Offset); } /// Dereferences the element at index \p I. @@ -693,18 +675,17 @@ public: template <typename T> T &elem(unsigned I) const { assert(isLive() && "Invalid pointer"); assert(isBlockPointer()); - assert(asBlockPointer().Pointee); + assert(BS.Pointee); assert(isDereferencable()); assert(getFieldDesc()->isPrimitiveArray()); + assert(I < getFieldDesc()->getNumElems()); unsigned ElemByteOffset = I * getFieldDesc()->getElemSize(); - if (isArrayRoot()) - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base + sizeof(InitMapPtr) + - ElemByteOffset); + unsigned ReadOffset = BS.Base + sizeof(InitMapPtr) + ElemByteOffset; + assert(ReadOffset + sizeof(T) <= + BS.Pointee->getDescriptor()->getAllocSize()); - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset + - ElemByteOffset); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + ReadOffset); } /// Whether this block can be read from at all. This is only true for @@ -724,6 +705,11 @@ public: /// used in situations where we *know* we have initialized *all* elements /// of a primtive array. void initializeAllElements() const; + /// Checks if an object was initialized. + bool isInitialized() const; + /// Like isInitialized(), but for primitive arrays. + bool isElementInitialized(unsigned Index) const; + bool allElementsInitialized() const; /// Activats a field. void activate() const; /// Deactivates an entire strurcutre. @@ -732,7 +718,7 @@ public: Lifetime getLifetime() const { if (!isBlockPointer()) return Lifetime::Started; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return Lifetime::Started; return getInlineDesc()->LifeState; } @@ -740,7 +726,7 @@ public: void endLifetime() const { if (!isBlockPointer()) return; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return; getInlineDesc()->LifeState = Lifetime::Ended; } @@ -748,7 +734,7 @@ public: void startLifetime() const { if (!isBlockPointer()) return; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return; getInlineDesc()->LifeState = Lifetime::Started; } @@ -801,10 +787,10 @@ private: /// Returns the embedded descriptor preceding a field. InlineDescriptor *getInlineDesc() const { assert(isBlockPointer()); - assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor)); - assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize()); - assert(asBlockPointer().Base >= sizeof(InlineDescriptor)); - return getDescriptor(asBlockPointer().Base); + assert(BS.Base != sizeof(GlobalInlineDescriptor)); + assert(BS.Base <= BS.Pointee->getSize()); + assert(BS.Base >= sizeof(InlineDescriptor)); + return getDescriptor(BS.Base); } /// Returns a descriptor at a given offset. @@ -812,8 +798,8 @@ private: assert(Offset != 0 && "Not a nested pointer"); assert(isBlockPointer()); assert(!isZero()); - return reinterpret_cast<InlineDescriptor *>( - asBlockPointer().Pointee->rawData() + Offset) - + return reinterpret_cast<InlineDescriptor *>(BS.Pointee->rawData() + + Offset) - 1; } @@ -821,8 +807,7 @@ private: InitMapPtr &getInitMap() const { assert(isBlockPointer()); assert(!isZero()); - return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base); + return *reinterpret_cast<InitMapPtr *>(BS.Pointee->rawData() + BS.Base); } /// Offset into the storage. diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h index 6e3a49cadcbf..54fd39ac6fcc 100644 --- a/clang/lib/AST/ByteCode/PrimType.h +++ b/clang/lib/AST/ByteCode/PrimType.h @@ -272,14 +272,4 @@ static inline bool aligned(const void *P) { } \ } while (0) -#define COMPOSITE_TYPE_SWITCH(Expr, B, D) \ - do { \ - switch (Expr) { \ - TYPE_SWITCH_CASE(PT_Ptr, B) \ - default: { \ - D; \ - break; \ - } \ - } \ - } while (0) #endif diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 5d72044af969..75bfd9fd2d8e 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -101,7 +101,7 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) { } } } - Ptr.initialize(); + Ptr.initializeAllElements(); return GlobalIndex; } @@ -111,7 +111,7 @@ Pointer Program::getPtrGlobal(unsigned Idx) const { return Pointer(Globals[Idx]->block()); } -std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { +UnsignedOrNone Program::getGlobal(const ValueDecl *VD) { if (auto It = GlobalIndices.find(VD); It != GlobalIndices.end()) return It->second; @@ -131,14 +131,14 @@ std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { return std::nullopt; } -std::optional<unsigned> Program::getGlobal(const Expr *E) { +UnsignedOrNone Program::getGlobal(const Expr *E) { if (auto It = GlobalIndices.find(E); It != GlobalIndices.end()) return It->second; return std::nullopt; } -std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD, - const Expr *Init) { +UnsignedOrNone Program::getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init) { if (auto Idx = getGlobal(VD)) return Idx; @@ -195,8 +195,7 @@ unsigned Program::getOrCreateDummy(const DeclTy &D) { return I; } -std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, - const Expr *Init) { +UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init) { bool IsStatic, IsExtern; bool IsWeak = VD->isWeak(); if (const auto *Var = dyn_cast<VarDecl>(VD)) { @@ -213,7 +212,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, // Register all previous declarations as well. For extern blocks, just replace // the index with the new variable. - std::optional<unsigned> Idx = + UnsignedOrNone Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, IsWeak, Init); if (!Idx) return std::nullopt; @@ -240,7 +239,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, return *Idx; } -std::optional<unsigned> Program::createGlobal(const Expr *E) { +UnsignedOrNone Program::createGlobal(const Expr *E) { if (auto Idx = getGlobal(E)) return Idx; if (auto Idx = createGlobal(E, E->getType(), /*isStatic=*/true, @@ -251,9 +250,9 @@ std::optional<unsigned> Program::createGlobal(const Expr *E) { return std::nullopt; } -std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern, - bool IsWeak, const Expr *Init) { +UnsignedOrNone Program::createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, bool IsWeak, + const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); @@ -332,10 +331,9 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { continue; // In error cases, the base might not be a RecordType. - const auto *RT = Spec.getType()->getAs<RecordType>(); - if (!RT) + const auto *BD = Spec.getType()->getAsCXXRecordDecl(); + if (!BD) return nullptr; - const RecordDecl *BD = RT->getOriginalDecl()->getDefinitionOrSelf(); const Record *BR = getOrCreateRecord(BD); const Descriptor *Desc = GetBaseDesc(BD, BR); @@ -348,11 +346,7 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { } for (const CXXBaseSpecifier &Spec : CD->vbases()) { - const auto *RT = Spec.getType()->getAs<RecordType>(); - if (!RT) - return nullptr; - - const RecordDecl *BD = RT->getOriginalDecl()->getDefinitionOrSelf(); + const auto *BD = Spec.getType()->castAsCXXRecordDecl(); const Record *BR = getOrCreateRecord(BD); const Descriptor *Desc = GetBaseDesc(BD, BR); @@ -408,9 +402,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, const Expr *Init) { // Classes and structures. - if (const auto *RT = Ty->getAs<RecordType>()) { - if (const auto *Record = - getOrCreateRecord(RT->getOriginalDecl()->getDefinitionOrSelf())) + if (const auto *RD = Ty->getAsRecordDecl()) { + if (const auto *Record = getOrCreateRecord(RD)) return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary, IsMutable, IsVolatile); return allocateDescriptor(D, MDSize); diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index 90b48ee5b669..28fcc97f5339 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -78,21 +78,21 @@ public: } /// Finds a global's index. - std::optional<unsigned> getGlobal(const ValueDecl *VD); - std::optional<unsigned> getGlobal(const Expr *E); + UnsignedOrNone getGlobal(const ValueDecl *VD); + UnsignedOrNone getGlobal(const Expr *E); /// Returns or creates a global an creates an index to it. - std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD, - const Expr *Init = nullptr); + UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init = nullptr); /// Returns or creates a dummy value for unknown declarations. unsigned getOrCreateDummy(const DeclTy &D); /// Creates a global and returns its index. - std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *Init); + UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init); /// Creates a global from a lifetime-extended temporary. - std::optional<unsigned> createGlobal(const Expr *E); + UnsignedOrNone createGlobal(const Expr *E); /// Creates a new function from a code range. template <typename... Ts> @@ -165,9 +165,9 @@ public: private: friend class DeclScope; - std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern, - bool IsWeak, const Expr *Init = nullptr); + UnsignedOrNone createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, + bool IsExtern, bool IsWeak, + const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; diff --git a/clang/lib/AST/ByteCode/Record.cpp b/clang/lib/AST/ByteCode/Record.cpp index a7934ccb4e55..ff5c82d24457 100644 --- a/clang/lib/AST/ByteCode/Record.cpp +++ b/clang/lib/AST/ByteCode/Record.cpp @@ -37,6 +37,13 @@ std::string Record::getName() const { return Ret; } +bool Record::hasTrivialDtor() const { + if (isAnonymousUnion()) + return true; + const CXXDestructorDecl *Dtor = getDestructor(); + return !Dtor || Dtor->isTrivial(); +} + const Record::Field *Record::getField(const FieldDecl *FD) const { auto It = FieldMap.find(FD->getFirstDecl()); assert(It != FieldMap.end() && "Missing field"); @@ -50,10 +57,8 @@ const Record::Base *Record::getBase(const RecordDecl *FD) const { } const Record::Base *Record::getBase(QualType T) const { - if (auto *RT = T->getAs<RecordType>()) { - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); + if (auto *RD = T->getAsCXXRecordDecl()) return BaseMap.lookup(RD); - } return nullptr; } diff --git a/clang/lib/AST/ByteCode/Record.h b/clang/lib/AST/ByteCode/Record.h index 93ca43046180..8245eeff2f20 100644 --- a/clang/lib/AST/ByteCode/Record.h +++ b/clang/lib/AST/ByteCode/Record.h @@ -76,6 +76,10 @@ public: return nullptr; } + /// Returns true for anonymous unions and records + /// with no destructor or for those with a trivial destructor. + bool hasTrivialDtor() const; + using const_field_iter = FieldList::const_iterator; llvm::iterator_range<const_field_iter> fields() const { return llvm::make_range(Fields.begin(), Fields.end()); diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index 6fc33222ac95..387ce396a723 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -59,8 +59,6 @@ class State { public: virtual ~State(); - virtual bool checkingForUndefinedBehavior() const = 0; - virtual bool checkingPotentialConstantExpression() const = 0; virtual bool noteUndefinedBehavior() = 0; virtual bool keepEvaluatingAfterFailure() const = 0; virtual bool keepEvaluatingAfterSideEffect() const = 0; @@ -75,6 +73,16 @@ public: virtual unsigned getCallStackDepth() = 0; virtual bool noteSideEffect() = 0; + /// Are we checking whether the expression is a potential constant + /// expression? + bool checkingPotentialConstantExpression() const { + return CheckingPotentialConstantExpression; + } + /// Are we checking an expression for overflow? + bool checkingForUndefinedBehavior() const { + return CheckingForUndefinedBehavior; + } + public: State() = default; /// Diagnose that the evaluation could not be folded (FF => FoldFailure) @@ -128,6 +136,19 @@ public: /// constant value. bool InConstantContext = false; + /// 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). + bool CheckingPotentialConstantExpression = false; + + /// Whether we're checking for an expression that has undefined behavior. + /// If so, we will produce warnings if we encounter an operation that is + /// always undefined. + /// + /// Note that we still need to evaluate the expression normally when this + /// is set; this is used when evaluating ICEs in C. + bool CheckingForUndefinedBehavior = false; + private: void addCallStack(unsigned Limit); diff --git a/clang/lib/AST/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp index e4b77edc063d..7a3e7ea4e5b8 100644 --- a/clang/lib/AST/CXXInheritance.cpp +++ b/clang/lib/AST/CXXInheritance.cpp @@ -128,17 +128,11 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback BaseMatches) const { const CXXRecordDecl *Record = this; while (true) { for (const auto &I : Record->bases()) { - const RecordType *Ty = I.getType()->getAs<RecordType>(); - if (!Ty) + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base || !(Base->isBeingDefined() || Base->isCompleteDefinition())) return false; - - CXXRecordDecl *Base = cast_if_present<CXXRecordDecl>( - Ty->getOriginalDecl()->getDefinition()); - if (!Base || - (Base->isDependentContext() && - !Base->isCurrentInstantiation(Record))) { + if (Base->isDependentContext() && !Base->isCurrentInstantiation(Record)) return false; - } Queue.push_back(Base); if (!BaseMatches(Base)) @@ -196,7 +190,7 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context, if (isDetectingVirtual() && DetectedVirtual == nullptr) { // If this is the first virtual we find, remember it. If it turns out // there is no base path here, we'll reset it later. - DetectedVirtual = BaseType->getAs<RecordType>(); + DetectedVirtual = BaseType->getAsCanonical<RecordType>(); SetVirtual = true; } } else { @@ -255,9 +249,7 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context, const TemplateSpecializationType *TST = BaseSpec.getType()->getAs<TemplateSpecializationType>(); if (!TST) { - if (auto *RT = BaseSpec.getType()->getAs<RecordType>()) - BaseRecord = cast<CXXRecordDecl>(RT->getOriginalDecl()) - ->getDefinitionOrSelf(); + BaseRecord = BaseSpec.getType()->getAsCXXRecordDecl(); } else { TemplateName TN = TST->getTemplateName(); if (auto *TD = @@ -271,7 +263,7 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context, BaseRecord = nullptr; } } else { - BaseRecord = cast<CXXRecordDecl>(BaseSpec.getType()->getAsRecordDecl()); + BaseRecord = BaseSpec.getType()->castAsCXXRecordDecl(); } if (BaseRecord && lookupInBases(Context, BaseRecord, BaseMatches, LookupInDependent)) { @@ -335,10 +327,7 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback BaseMatches, if (!PE.Base->isVirtual()) continue; - CXXRecordDecl *VBase = nullptr; - if (const RecordType *Record = PE.Base->getType()->getAs<RecordType>()) - VBase = cast<CXXRecordDecl>(Record->getOriginalDecl()) - ->getDefinitionOrSelf(); + auto *VBase = PE.Base->getType()->getAsCXXRecordDecl(); if (!VBase) break; @@ -347,11 +336,8 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback BaseMatches, // base is a subobject of any other path; if so, then the // declaration in this path are hidden by that patch. for (const CXXBasePath &HidingP : Paths) { - CXXRecordDecl *HidingClass = nullptr; - if (const RecordType *Record = - HidingP.back().Base->getType()->getAs<RecordType>()) - HidingClass = cast<CXXRecordDecl>(Record->getOriginalDecl()) - ->getDefinitionOrSelf(); + auto *HidingClass = + HidingP.back().Base->getType()->getAsCXXRecordDecl(); if (!HidingClass) break; @@ -407,7 +393,7 @@ bool CXXRecordDecl::hasMemberName(DeclarationName Name) const { CXXBasePaths Paths(false, false, false); return lookupInBases( [Name](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { - return findOrdinaryMember(Specifier->getType()->getAsCXXRecordDecl(), + return findOrdinaryMember(Specifier->getType()->castAsCXXRecordDecl(), Path, Name); }, Paths); @@ -470,9 +456,7 @@ void FinalOverriderCollector::Collect(const CXXRecordDecl *RD, = ++SubobjectCount[cast<CXXRecordDecl>(RD->getCanonicalDecl())]; for (const auto &Base : RD->bases()) { - if (const RecordType *RT = Base.getType()->getAs<RecordType>()) { - const CXXRecordDecl *BaseDecl = - cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(); + if (const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl()) { if (!BaseDecl->isPolymorphic()) continue; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 4507f415ce60..d8dffb7f5dc4 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2861,9 +2861,8 @@ VarDecl::needsDestruction(const ASTContext &Ctx) const { bool VarDecl::hasFlexibleArrayInit(const ASTContext &Ctx) const { assert(hasInit() && "Expect initializer to check for flexible array init"); - auto *Ty = getType()->getAs<RecordType>(); - if (!Ty || - !Ty->getOriginalDecl()->getDefinitionOrSelf()->hasFlexibleArrayMember()) + auto *D = getType()->getAsRecordDecl(); + if (!D || !D->hasFlexibleArrayMember()) return false; auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens()); if (!List) @@ -2877,11 +2876,8 @@ bool VarDecl::hasFlexibleArrayInit(const ASTContext &Ctx) const { CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const { assert(hasInit() && "Expect initializer to check for flexible array init"); - auto *Ty = getType()->getAs<RecordType>(); - if (!Ty) - return CharUnits::Zero(); - const RecordDecl *RD = Ty->getOriginalDecl()->getDefinitionOrSelf(); - if (!Ty || !RD->hasFlexibleArrayMember()) + auto *RD = getType()->getAsRecordDecl(); + if (!RD || !RD->hasFlexibleArrayMember()) return CharUnits::Zero(); auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens()); if (!List || List->getNumInits() == 0) @@ -2992,7 +2988,7 @@ bool ParmVarDecl::isDestroyedInCallee() const { // FIXME: isParamDestroyedInCallee() should probably imply // isDestructedType() - const auto *RT = getType()->getAs<RecordType>(); + const auto *RT = getType()->getAsCanonical<RecordType>(); if (RT && RT->getOriginalDecl() ->getDefinitionOrSelf() @@ -3507,7 +3503,7 @@ bool FunctionDecl::isUsableAsGlobalAllocationFunctionInConstantEvaluation( while (const auto *TD = T->getAs<TypedefType>()) T = TD->getDecl()->getUnderlyingType(); const IdentifierInfo *II = - T->castAs<EnumType>()->getOriginalDecl()->getIdentifier(); + T->castAsCanonical<EnumType>()->getOriginalDecl()->getIdentifier(); if (II && II->isStr("__hot_cold_t")) Consume(); } @@ -3604,6 +3600,10 @@ bool FunctionDecl::isNoReturn() const { return false; } +bool FunctionDecl::isAnalyzerNoReturn() const { + return hasAttr<AnalyzerNoReturnAttr>(); +} + bool FunctionDecl::isMemberLikeConstrainedFriend() const { // C++20 [temp.friend]p9: // A non-template friend declaration with a requires-clause [or] @@ -4657,7 +4657,7 @@ bool FieldDecl::isAnonymousStructOrUnion() const { if (!isImplicit() || getDeclName()) return false; - if (const auto *Record = getType()->getAs<RecordType>()) + if (const auto *Record = getType()->getAsCanonical<RecordType>()) return Record->getOriginalDecl()->isAnonymousStructOrUnion(); return false; @@ -4715,7 +4715,7 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const { return false; // -- is not of class type, or - const auto *RT = getType()->getAs<RecordType>(); + const auto *RT = getType()->getAsCanonical<RecordType>(); if (!RT) return false; const RecordDecl *RD = RT->getOriginalDecl()->getDefinition(); @@ -4738,7 +4738,7 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const { // MS ABI: has nonzero size if it is a class type with class type fields, // whether or not they have nonzero size return !llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) { - return Field->getType()->getAs<RecordType>(); + return Field->getType()->isRecordType(); }); } @@ -5142,7 +5142,7 @@ bool RecordDecl::isOrContainsUnion() const { if (const RecordDecl *Def = getDefinition()) { for (const FieldDecl *FD : Def->fields()) { - const RecordType *RT = FD->getType()->getAs<RecordType>(); + const RecordType *RT = FD->getType()->getAsCanonical<RecordType>(); if (RT && RT->getOriginalDecl()->isOrContainsUnion()) return true; } @@ -5274,10 +5274,8 @@ const FieldDecl *RecordDecl::findFirstNamedDataMember() const { if (I->getIdentifier()) return I; - if (const auto *RT = I->getType()->getAs<RecordType>()) - if (const FieldDecl *NamedDataMember = RT->getOriginalDecl() - ->getDefinitionOrSelf() - ->findFirstNamedDataMember()) + if (const auto *RD = I->getType()->getAsRecordDecl()) + if (const FieldDecl *NamedDataMember = RD->findFirstNamedDataMember()) return NamedDataMember; } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 50b1a1d00009..aa1f5a114659 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -216,9 +216,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // Skip dependent types; we can't do any checking on them now. if (BaseType->isDependentType()) continue; - auto *BaseClassDecl = - cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + auto *BaseClassDecl = BaseType->castAsCXXRecordDecl(); // C++2a [class]p7: // A standard-layout class is a class that: @@ -1207,9 +1205,8 @@ void CXXRecordDecl::addedMember(Decl *D) { // those because they are always unnamed. bool IsZeroSize = Field->isZeroSize(Context); - if (const auto *RecordTy = T->getAs<RecordType>()) { - auto *FieldRec = cast<CXXRecordDecl>(RecordTy->getOriginalDecl()); - if (FieldRec->getDefinition()) { + if (auto *FieldRec = T->getAsCXXRecordDecl()) { + if (FieldRec->isBeingDefined() || FieldRec->isCompleteDefinition()) { addedClassSubobject(FieldRec); // We may need to perform overload resolution to determine whether a @@ -1438,6 +1435,13 @@ void CXXRecordDecl::addedMember(Decl *D) { data().StructuralIfLiteral = false; } + // If this type contains any address discriminated values we should + // have already indicated that the only special member functions that + // can possibly be trivial are the default constructor and destructor. + if (T.hasAddressDiscriminatedPointerAuth()) + data().HasTrivialSpecialMembers &= + SMF_DefaultConstructor | SMF_Destructor; + // C++14 [meta.unary.prop]p4: // T is a class type [...] with [...] no non-static data members other // than subobjects of zero size @@ -1908,15 +1912,14 @@ static void CollectVisibleConversions( // Collect information recursively from any base classes. for (const auto &I : Record->bases()) { - const auto *RT = I.getType()->getAs<RecordType>(); - if (!RT) continue; + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base) + continue; AccessSpecifier BaseAccess = CXXRecordDecl::MergeAccess(Access, I.getAccessSpecifier()); bool BaseInVirtual = InVirtual || I.isVirtual(); - auto *Base = - cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(); CollectVisibleConversions(Context, Base, BaseInVirtual, BaseAccess, *HiddenTypes, Output, VOutput, HiddenVBaseCs); } @@ -1951,14 +1954,13 @@ static void CollectVisibleConversions(ASTContext &Context, // Recursively collect conversions from base classes. for (const auto &I : Record->bases()) { - const auto *RT = I.getType()->getAs<RecordType>(); - if (!RT) continue; + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base) + continue; - CollectVisibleConversions( - Context, - cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(), - I.isVirtual(), I.getAccessSpecifier(), HiddenTypes, Output, VBaseCs, - HiddenVBaseCs); + CollectVisibleConversions(Context, Base, I.isVirtual(), + I.getAccessSpecifier(), HiddenTypes, Output, + VBaseCs, HiddenVBaseCs); } // Add any unhidden conversions provided by virtual bases. @@ -2312,7 +2314,7 @@ bool CXXRecordDecl::mayBeAbstract() const { for (const auto &B : bases()) { const auto *BaseDecl = cast<CXXRecordDecl>( - B.getType()->castAs<RecordType>()->getOriginalDecl()); + B.getType()->castAsCanonical<RecordType>()->getOriginalDecl()); if (BaseDecl->isAbstract()) return true; } @@ -2472,11 +2474,9 @@ CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, }; for (const auto &I : RD->bases()) { - const RecordType *RT = I.getType()->getAs<RecordType>(); - if (!RT) + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base) continue; - const auto *Base = - cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(); if (CXXMethodDecl *D = this->getCorrespondingMethodInClass(Base)) AddFinalOverrider(D); } @@ -3437,13 +3437,12 @@ SourceRange UsingDecl::getSourceRange() const { void UsingEnumDecl::anchor() {} UsingEnumDecl *UsingEnumDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation UL, - SourceLocation EL, + 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(DC, EnumType->getType()->castAsEnumDecl()->getDeclName(), + UL, EL, NL, EnumType); } UsingEnumDecl *UsingEnumDecl::CreateDeserialized(ASTContext &C, diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index b91b4670c63a..3162857aac5d 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -730,15 +730,15 @@ void TemplateTypeParmDecl::setDefaultArgument( } unsigned TemplateTypeParmDecl::getDepth() const { - return getTypeForDecl()->castAs<TemplateTypeParmType>()->getDepth(); + return dyn_cast<TemplateTypeParmType>(getTypeForDecl())->getDepth(); } unsigned TemplateTypeParmDecl::getIndex() const { - return getTypeForDecl()->castAs<TemplateTypeParmType>()->getIndex(); + return dyn_cast<TemplateTypeParmType>(getTypeForDecl())->getIndex(); } bool TemplateTypeParmDecl::isParameterPack() const { - return getTypeForDecl()->castAs<TemplateTypeParmType>()->isParameterPack(); + return dyn_cast<TemplateTypeParmType>(getTypeForDecl())->isParameterPack(); } void TemplateTypeParmDecl::setTypeConstraint( diff --git a/clang/lib/AST/DeclarationName.cpp b/clang/lib/AST/DeclarationName.cpp index 6c7b995d5756..55f5a994d788 100644 --- a/clang/lib/AST/DeclarationName.cpp +++ b/clang/lib/AST/DeclarationName.cpp @@ -113,13 +113,14 @@ static void printCXXConstructorDestructorName(QualType ClassType, PrintingPolicy Policy) { // We know we're printing C++ here. Ensure we print types properly. Policy.adjustForCPlusPlus(); + Policy.SuppressScope = true; - if (const RecordType *ClassRec = ClassType->getAs<RecordType>()) { + if (const RecordType *ClassRec = ClassType->getAsCanonical<RecordType>()) { ClassRec->getOriginalDecl()->printName(OS, Policy); return; } if (Policy.SuppressTemplateArgsInCXXConstructors) { - if (auto *InjTy = ClassType->getAs<InjectedClassNameType>()) { + if (auto *InjTy = ClassType->getAsCanonical<InjectedClassNameType>()) { InjTy->getOriginalDecl()->printName(OS, Policy); return; } diff --git a/clang/lib/AST/DynamicRecursiveASTVisitor.cpp b/clang/lib/AST/DynamicRecursiveASTVisitor.cpp index 8821cd332e91..6d7925b437b0 100644 --- a/clang/lib/AST/DynamicRecursiveASTVisitor.cpp +++ b/clang/lib/AST/DynamicRecursiveASTVisitor.cpp @@ -87,7 +87,7 @@ using namespace clang; // ends up executing RAV's implementation because we used a qualified // function call. // -// End result: RAV::TraverseCallExpr() is executed, +// End result: RAV::TraverseCallExpr() is executed. namespace { template <bool Const> struct Impl : RecursiveASTVisitor<Impl<Const>> { DynamicRecursiveASTVisitorBase<Const> &Visitor; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 340de6d4be93..cdff160067fe 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -74,8 +74,7 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const { if (DerivedType->isDependentType()) return nullptr; - const RecordType *Ty = DerivedType->castAs<RecordType>(); - return cast<CXXRecordDecl>(Ty->getOriginalDecl())->getDefinitionOrSelf(); + return DerivedType->castAsCXXRecordDecl(); } const Expr *Expr::skipRValueSubobjectAdjustments( @@ -90,10 +89,7 @@ const Expr *Expr::skipRValueSubobjectAdjustments( CE->getCastKind() == CK_UncheckedDerivedToBase) && E->getType()->isRecordType()) { E = CE->getSubExpr(); - const auto *Derived = - cast<CXXRecordDecl>( - E->getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *Derived = E->getType()->castAsCXXRecordDecl(); Adjustments.push_back(SubobjectAdjustment(CE, Derived)); continue; } @@ -2032,9 +2028,7 @@ CXXBaseSpecifier **CastExpr::path_buffer() { const FieldDecl *CastExpr::getTargetFieldForToUnionCast(QualType unionType, QualType opType) { - auto RD = - unionType->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); - return getTargetFieldForToUnionCast(RD, opType); + return getTargetFieldForToUnionCast(unionType->castAsRecordDecl(), opType); } const FieldDecl *CastExpr::getTargetFieldForToUnionCast(const RecordDecl *RD, @@ -2401,6 +2395,7 @@ EmbedExpr::EmbedExpr(const ASTContext &Ctx, SourceLocation Loc, setDependence(ExprDependence::None); FakeChildNode = IntegerLiteral::Create( Ctx, llvm::APInt::getZero(Ctx.getTypeSize(getType())), getType(), Loc); + assert(getType()->isSignedIntegerType() && "IntTy should be signed"); } InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, @@ -3396,10 +3391,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, if (ILE->getType()->isRecordType()) { unsigned ElementNo = 0; - RecordDecl *RD = ILE->getType() - ->castAs<RecordType>() - ->getOriginalDecl() - ->getDefinitionOrSelf(); + auto *RD = ILE->getType()->castAsRecordDecl(); // In C++17, bases were added to the list of members used by aggregate // initialization. @@ -3533,6 +3525,56 @@ bool CallExpr::isBuiltinAssumeFalse(const ASTContext &Ctx) const { Arg->EvaluateAsBooleanCondition(ArgVal, Ctx) && !ArgVal; } +const AllocSizeAttr *CallExpr::getCalleeAllocSizeAttr() const { + if (const FunctionDecl *DirectCallee = getDirectCallee()) + return DirectCallee->getAttr<AllocSizeAttr>(); + if (const Decl *IndirectCallee = getCalleeDecl()) + return IndirectCallee->getAttr<AllocSizeAttr>(); + return nullptr; +} + +std::optional<llvm::APInt> +CallExpr::evaluateBytesReturnedByAllocSizeCall(const ASTContext &Ctx) const { + const AllocSizeAttr *AllocSize = getCalleeAllocSizeAttr(); + + assert(AllocSize && AllocSize->getElemSizeParam().isValid()); + unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex(); + unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType()); + if (getNumArgs() <= SizeArgNo) + return std::nullopt; + + auto EvaluateAsSizeT = [&](const Expr *E, llvm::APSInt &Into) { + Expr::EvalResult ExprResult; + if (E->isValueDependent() || + !E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects)) + return false; + Into = ExprResult.Val.getInt(); + if (Into.isNegative() || !Into.isIntN(BitsInSizeT)) + return false; + Into = Into.zext(BitsInSizeT); + return true; + }; + + llvm::APSInt SizeOfElem; + if (!EvaluateAsSizeT(getArg(SizeArgNo), SizeOfElem)) + return std::nullopt; + + if (!AllocSize->getNumElemsParam().isValid()) + return SizeOfElem; + + llvm::APSInt NumberOfElems; + unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex(); + if (!EvaluateAsSizeT(getArg(NumArgNo), NumberOfElems)) + return std::nullopt; + + bool Overflow; + llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow); + if (Overflow) + return std::nullopt; + + return BytesAvailable; +} + bool CallExpr::isCallToStdMove() const { return getBuiltinCallee() == Builtin::BImove; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a71cb8b0143b..2376e482a19f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -114,15 +114,6 @@ namespace { return Ctx.getLValueReferenceType(E->getType()); } - /// Given a CallExpr, try to get the alloc_size attribute. May return null. - static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) { - if (const FunctionDecl *DirectCallee = CE->getDirectCallee()) - return DirectCallee->getAttr<AllocSizeAttr>(); - if (const Decl *IndirectCallee = CE->getCalleeDecl()) - return IndirectCallee->getAttr<AllocSizeAttr>(); - return nullptr; - } - /// Attempts to unwrap a CallExpr (with an alloc_size attribute) from an Expr. /// This will look through a single cast. /// @@ -142,7 +133,7 @@ namespace { E = Cast->getSubExpr()->IgnoreParens(); if (const auto *CE = dyn_cast<CallExpr>(E)) - return getAllocSizeAttr(CE) ? CE : nullptr; + return CE->getCalleeAllocSizeAttr() ? CE : nullptr; return nullptr; } @@ -465,49 +456,7 @@ namespace { void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, const APSInt &N); /// Add N to the address of this subobject. - void adjustIndex(EvalInfo &Info, const Expr *E, APSInt N) { - if (Invalid || !N) return; - uint64_t TruncatedN = N.extOrTrunc(64).getZExtValue(); - if (isMostDerivedAnUnsizedArray()) { - diagnoseUnsizedArrayPointerArithmetic(Info, E); - // Can't verify -- trust that the user is doing the right thing (or if - // not, trust that the caller will catch the bad behavior). - // FIXME: Should we reject if this overflows, at least? - Entries.back() = PathEntry::ArrayIndex( - Entries.back().getAsArrayIndex() + TruncatedN); - return; - } - - // [expr.add]p4: For the purposes of these operators, a pointer to a - // nonarray object behaves the same as a pointer to the first element of - // an array of length one with the type of the object as its element type. - bool IsArray = MostDerivedPathLength == Entries.size() && - MostDerivedIsArrayElement; - uint64_t ArrayIndex = IsArray ? Entries.back().getAsArrayIndex() - : (uint64_t)IsOnePastTheEnd; - uint64_t ArraySize = - IsArray ? getMostDerivedArraySize() : (uint64_t)1; - - if (N < -(int64_t)ArrayIndex || N > ArraySize - ArrayIndex) { - // Calculate the actual index in a wide enough type, so we can include - // it in the note. - N = N.extend(std::max<unsigned>(N.getBitWidth() + 1, 65)); - (llvm::APInt&)N += ArrayIndex; - assert(N.ugt(ArraySize) && "bounds check failed for in-bounds index"); - diagnosePointerArithmetic(Info, E, N); - setInvalid(); - return; - } - - ArrayIndex += TruncatedN; - assert(ArrayIndex <= ArraySize && - "bounds check succeeded for out-of-bounds index"); - - if (IsArray) - Entries.back() = PathEntry::ArrayIndex(ArrayIndex); - else - IsOnePastTheEnd = (ArrayIndex != 0); - } + void adjustIndex(EvalInfo &Info, const Expr *E, APSInt N, const LValue &LV); }; /// A scope at the end of which an object can need to be destroyed. @@ -894,6 +843,11 @@ namespace { /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; + /// Stack of loops and 'switch' statements which we're currently + /// breaking/continuing; null entries are used to mark unlabeled + /// break/continue. + SmallVector<const Stmt *> BreakContinueStack; + /// Set of objects that are currently being constructed. llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase> ObjectsUnderConstruction; @@ -972,19 +926,6 @@ namespace { /// fold (not just why it's not strictly a constant expression)? bool HasFoldFailureDiagnostic; - /// 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). - bool CheckingPotentialConstantExpression = false; - - /// Whether we're checking for an expression that has undefined behavior. - /// If so, we will produce warnings if we encounter an operation that is - /// always undefined. - /// - /// Note that we still need to evaluate the expression normally when this - /// is set; this is used when evaluating ICEs in C. - bool CheckingForUndefinedBehavior = false; - enum EvaluationMode { /// Evaluate as a constant expression. Stop if we find that the expression /// is not a constant expression. @@ -1006,19 +947,6 @@ namespace { EM_IgnoreSideEffects, } EvalMode; - /// Are we checking whether the expression is a potential constant - /// expression? - bool checkingPotentialConstantExpression() const override { - return CheckingPotentialConstantExpression; - } - - /// Are we checking an expression for overflow? - // FIXME: We should check for any kind of undefined or suspicious behavior - // in such constructs, not just overflow. - bool checkingForUndefinedBehavior() const override { - return CheckingForUndefinedBehavior; - } - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), @@ -1799,7 +1727,7 @@ namespace { Offset = CharUnits::fromQuantity(Offset64 + ElemSize64 * Index64); if (checkNullPointer(Info, E, CSK_ArrayIndex)) - Designator.adjustIndex(Info, E, Index); + Designator.adjustIndex(Info, E, Index, *this); clearIsNullPointer(); } void adjustOffset(CharUnits N) { @@ -1907,6 +1835,54 @@ namespace { } } +void SubobjectDesignator::adjustIndex(EvalInfo &Info, const Expr *E, APSInt N, + const LValue &LV) { + if (Invalid || !N) + return; + uint64_t TruncatedN = N.extOrTrunc(64).getZExtValue(); + if (isMostDerivedAnUnsizedArray()) { + diagnoseUnsizedArrayPointerArithmetic(Info, E); + // Can't verify -- trust that the user is doing the right thing (or if + // not, trust that the caller will catch the bad behavior). + // FIXME: Should we reject if this overflows, at least? + Entries.back() = + PathEntry::ArrayIndex(Entries.back().getAsArrayIndex() + TruncatedN); + return; + } + + // [expr.add]p4: For the purposes of these operators, a pointer to a + // nonarray object behaves the same as a pointer to the first element of + // an array of length one with the type of the object as its element type. + bool IsArray = + MostDerivedPathLength == Entries.size() && MostDerivedIsArrayElement; + uint64_t ArrayIndex = + IsArray ? Entries.back().getAsArrayIndex() : (uint64_t)IsOnePastTheEnd; + uint64_t ArraySize = IsArray ? getMostDerivedArraySize() : (uint64_t)1; + + if (N < -(int64_t)ArrayIndex || N > ArraySize - ArrayIndex) { + if (!Info.checkingPotentialConstantExpression() || + !LV.AllowConstexprUnknown) { + // Calculate the actual index in a wide enough type, so we can include + // it in the note. + N = N.extend(std::max<unsigned>(N.getBitWidth() + 1, 65)); + (llvm::APInt &)N += ArrayIndex; + assert(N.ugt(ArraySize) && "bounds check failed for in-bounds index"); + diagnosePointerArithmetic(Info, E, N); + } + setInvalid(); + return; + } + + ArrayIndex += TruncatedN; + assert(ArrayIndex <= ArraySize && + "bounds check succeeded for out-of-bounds index"); + + if (IsArray) + Entries.back() = PathEntry::ArrayIndex(ArrayIndex); + else + IsOnePastTheEnd = (ArrayIndex != 0); +} + static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E); static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, const Expr *E, @@ -2623,8 +2599,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, Value.getUnionValue(), Kind, Value.getUnionField(), CheckedTemps); } if (Value.isStruct()) { - RecordDecl *RD = - Type->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); + auto *RD = Type->castAsRecordDecl(); if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) { unsigned BaseIndex = 0; for (const CXXBaseSpecifier &BS : CD->bases()) { @@ -4029,10 +4004,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, LastField = nullptr; if (ObjType->isArrayType()) { // Next subobject is an array element. - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType); - assert(CAT && "vla in literal type?"); + const ArrayType *AT = Info.Ctx.getAsArrayType(ObjType); + assert((isa<ConstantArrayType>(AT) || isa<IncompleteArrayType>(AT)) && + "vla in literal type?"); uint64_t Index = Sub.Entries[I].getAsArrayIndex(); - if (CAT->getSize().ule(Index)) { + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT); + CAT && CAT->getSize().ule(Index)) { // Note, it should not be possible to form a pointer with a valid // designator which points more than one past the end of the array. if (Info.getLangOpts().CPlusPlus11) @@ -4043,12 +4020,13 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, return handler.failed(); } - ObjType = CAT->getElementType(); + ObjType = AT->getElementType(); if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); else if (!isRead(handler.AccessKind)) { - if (!CheckArraySize(Info, CAT, E->getExprLoc())) + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT); + CAT && !CheckArraySize(Info, CAT, E->getExprLoc())) return handler.failed(); expandArray(*O, Index); @@ -4110,7 +4088,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, } // Next subobject is a class, struct or union field. - RecordDecl *RD = ObjType->castAs<RecordType>()->getOriginalDecl(); + RecordDecl *RD = + ObjType->castAsCanonical<RecordType>()->getOriginalDecl(); if (RD->isUnion()) { const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || @@ -5148,12 +5127,18 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, if (const PointerType *PT = TargetQT->getAs<PointerType>()) TargetQT = PT->getPointeeType(); - // Check this cast lands within the final derived-to-base subobject path. - if (D.MostDerivedPathLength + E->path_size() > D.Entries.size()) { - Info.CCEDiag(E, diag::note_constexpr_invalid_downcast) - << D.MostDerivedType << TargetQT; + auto InvalidCast = [&]() { + if (!Info.checkingPotentialConstantExpression() || + !Result.AllowConstexprUnknown) { + Info.CCEDiag(E, diag::note_constexpr_invalid_downcast) + << D.MostDerivedType << TargetQT; + } return false; - } + }; + + // Check this cast lands within the final derived-to-base subobject path. + if (D.MostDerivedPathLength + E->path_size() > D.Entries.size()) + return InvalidCast(); // Check the type of the final cast. We don't need to check the path, // since a cast can only be formed if the path is unique. @@ -5164,11 +5149,8 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, FinalType = D.MostDerivedType->getAsCXXRecordDecl(); else FinalType = getAsBaseClass(D.Entries[NewEntriesSize - 1]); - if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl()) { - Info.CCEDiag(E, diag::note_constexpr_invalid_downcast) - << D.MostDerivedType << TargetQT; - return false; - } + if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl()) + return InvalidCast(); // Truncate the lvalue to the appropriate derived class. return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize); @@ -5386,6 +5368,44 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, const Stmt *S, const SwitchCase *SC = nullptr); +/// Helper to implement named break/continue. Returns 'true' if the evaluation +/// result should be propagated up. Otherwise, it sets the evaluation result +/// to either Continue to continue the current loop, or Succeeded to break it. +static bool ShouldPropagateBreakContinue(EvalInfo &Info, + const Stmt *LoopOrSwitch, + ArrayRef<BlockScopeRAII *> Scopes, + EvalStmtResult &ESR) { + bool IsSwitch = isa<SwitchStmt>(LoopOrSwitch); + + // For loops, map Succeeded to Continue so we don't have to check for both. + if (!IsSwitch && ESR == ESR_Succeeded) { + ESR = ESR_Continue; + return false; + } + + if (ESR != ESR_Break && ESR != ESR_Continue) + return false; + + // Are we breaking out of or continuing this statement? + bool CanBreakOrContinue = !IsSwitch || ESR == ESR_Break; + const Stmt *StackTop = Info.BreakContinueStack.back(); + if (CanBreakOrContinue && (StackTop == nullptr || StackTop == LoopOrSwitch)) { + Info.BreakContinueStack.pop_back(); + if (ESR == ESR_Break) + ESR = ESR_Succeeded; + return false; + } + + // We're not. Propagate the result up. + for (BlockScopeRAII *S : Scopes) { + if (!S->destroy()) { + ESR = ESR_Failed; + break; + } + } + return true; +} + /// Evaluate the body of a loop, and translate the result as appropriate. static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info, const Stmt *Body, @@ -5396,18 +5416,7 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info, if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) ESR = ESR_Failed; - switch (ESR) { - case ESR_Break: - return ESR_Succeeded; - case ESR_Succeeded: - case ESR_Continue: - return ESR_Continue; - case ESR_Failed: - case ESR_Returned: - case ESR_CaseNotFound: - return ESR; - } - llvm_unreachable("Invalid EvalStmtResult!"); + return ESR; } /// Evaluate a switch statement. @@ -5473,10 +5482,12 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found); if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) return ESR_Failed; + if (ShouldPropagateBreakContinue(Info, SS, /*Scopes=*/{}, ESR)) + return ESR; switch (ESR) { case ESR_Break: - return ESR_Succeeded; + llvm_unreachable("Should have been converted to Succeeded"); case ESR_Succeeded: case ESR_Continue: case ESR_Failed: @@ -5574,6 +5585,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::WhileStmtClass: { EvalStmtResult ESR = EvaluateLoopBody(Result, Info, cast<WhileStmt>(S)->getBody(), Case); + if (ShouldPropagateBreakContinue(Info, S, /*Scopes=*/{}, ESR)) + return ESR; if (ESR != ESR_Continue) return ESR; break; @@ -5595,6 +5608,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody(), Case); + if (ShouldPropagateBreakContinue(Info, FS, /*Scopes=*/{}, ESR)) + return ESR; if (ESR != ESR_Continue) return ESR; if (const auto *Inc = FS->getInc()) { @@ -5757,6 +5772,9 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, break; EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody()); + if (ShouldPropagateBreakContinue(Info, WS, &Scope, ESR)) + return ESR; + if (ESR != ESR_Continue) { if (ESR != ESR_Failed && !Scope.destroy()) return ESR_Failed; @@ -5773,6 +5791,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, bool Continue; do { EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody(), Case); + if (ShouldPropagateBreakContinue(Info, DS, /*Scopes=*/{}, ESR)) + return ESR; if (ESR != ESR_Continue) return ESR; Case = nullptr; @@ -5815,6 +5835,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, } EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody()); + if (ShouldPropagateBreakContinue(Info, FS, {&IterScope, &ForScope}, ESR)) + return ESR; if (ESR != ESR_Continue) { if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy())) return ESR_Failed; @@ -5906,6 +5928,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // Loop body. ESR = EvaluateLoopBody(Result, Info, FS->getBody()); + if (ShouldPropagateBreakContinue(Info, FS, {&InnerScope, &Scope}, ESR)) + return ESR; if (ESR != ESR_Continue) { if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy())) return ESR_Failed; @@ -5931,10 +5955,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, return EvaluateSwitch(Result, Info, cast<SwitchStmt>(S)); case Stmt::ContinueStmtClass: - return ESR_Continue; - - case Stmt::BreakStmtClass: - return ESR_Break; + case Stmt::BreakStmtClass: { + auto *B = cast<LoopControlStmt>(S); + Info.BreakContinueStack.push_back(B->getNamedLoopOrSwitch()); + return isa<ContinueStmt>(S) ? ESR_Continue : ESR_Break; + } case Stmt::LabelStmtClass: return EvaluateStmt(Result, Info, cast<LabelStmt>(S)->getSubStmt(), Case); @@ -6126,12 +6151,15 @@ static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This, } else if (Polymorphic) { // Conservatively refuse to perform a polymorphic operation if we would // not be able to read a notional 'vptr' value. - APValue Val; - This.moveInto(Val); - QualType StarThisType = - Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx)); - Info.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type) - << AK << Val.getAsString(Info.Ctx, StarThisType); + if (!Info.checkingPotentialConstantExpression() || + !This.AllowConstexprUnknown) { + APValue Val; + This.moveInto(Val); + QualType StarThisType = + Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx)); + Info.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type) + << AK << Val.getAsString(Info.Ctx, StarThisType); + } return false; } return true; @@ -7984,8 +8012,9 @@ static bool checkBitCastConstexprEligibilityType(SourceLocation Loc, // so its layout is unspecified. For now, we'll simply treat these cases // as unsupported (this should only be possible with OpenCL bool vectors // whose element count isn't a multiple of the byte size). - Info->FFDiag(Loc, diag::note_constexpr_bit_cast_invalid_vector) - << QualType(VTy, 0) << EltSize << NElts << Ctx.getCharWidth(); + if (Info) + Info->FFDiag(Loc, diag::note_constexpr_bit_cast_invalid_vector) + << QualType(VTy, 0) << EltSize << NElts << Ctx.getCharWidth(); return false; } @@ -7994,8 +8023,9 @@ static bool checkBitCastConstexprEligibilityType(SourceLocation Loc, // The layout for x86_fp80 vectors seems to be handled very inconsistently // by both clang and LLVM, so for now we won't allow bit_casts involving // it in a constexpr context. - Info->FFDiag(Loc, diag::note_constexpr_bit_cast_unsupported_type) - << EltTy; + if (Info) + Info->FFDiag(Loc, diag::note_constexpr_bit_cast_unsupported_type) + << EltTy; return false; } } @@ -8591,10 +8621,9 @@ public: const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl()); if (!FD) return Error(E); assert(!FD->getType()->isReferenceType() && "prvalue reference?"); - assert( - BaseTy->castAs<RecordType>()->getOriginalDecl()->getCanonicalDecl() == - FD->getParent()->getCanonicalDecl() && - "record / field mismatch"); + assert(BaseTy->castAsCanonical<RecordType>()->getOriginalDecl() == + FD->getParent()->getCanonicalDecl() && + "record / field mismatch"); // Note: there is no lvalue base here. But this case should only ever // happen in C or in C++98, where we cannot be evaluating a constexpr @@ -8821,10 +8850,9 @@ public: const ValueDecl *MD = E->getMemberDecl(); if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) { - assert( - BaseTy->castAs<RecordType>()->getOriginalDecl()->getCanonicalDecl() == - FD->getParent()->getCanonicalDecl() && - "record / field mismatch"); + assert(BaseTy->castAsCanonical<RecordType>()->getOriginalDecl() == + FD->getParent()->getCanonicalDecl() && + "record / field mismatch"); (void)BaseTy; if (!HandleLValueMember(this->Info, E, Result, FD)) return false; @@ -9466,57 +9494,6 @@ bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { // Pointer Evaluation //===----------------------------------------------------------------------===// -/// Attempts to compute the number of bytes available at the pointer -/// returned by a function with the alloc_size attribute. Returns true if we -/// were successful. Places an unsigned number into `Result`. -/// -/// This expects the given CallExpr to be a call to a function with an -/// alloc_size attribute. -static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, - const CallExpr *Call, - llvm::APInt &Result) { - const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call); - - assert(AllocSize && AllocSize->getElemSizeParam().isValid()); - unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex(); - unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType()); - if (Call->getNumArgs() <= SizeArgNo) - return false; - - auto EvaluateAsSizeT = [&](const Expr *E, APSInt &Into) { - Expr::EvalResult ExprResult; - if (!E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects)) - return false; - Into = ExprResult.Val.getInt(); - if (Into.isNegative() || !Into.isIntN(BitsInSizeT)) - return false; - Into = Into.zext(BitsInSizeT); - return true; - }; - - APSInt SizeOfElem; - if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem)) - return false; - - if (!AllocSize->getNumElemsParam().isValid()) { - Result = std::move(SizeOfElem); - return true; - } - - APSInt NumberOfElems; - unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex(); - if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems)) - return false; - - bool Overflow; - llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow); - if (Overflow) - return false; - - Result = std::move(BytesAvailable); - return true; -} - /// Convenience function. LVal's base must be a call to an alloc_size /// function. static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, @@ -9526,7 +9503,13 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, "Can't get the size of a non alloc_size function"); const auto *Base = LVal.getLValueBase().get<const Expr *>(); const CallExpr *CE = tryUnwrapAllocSizeCall(Base); - return getBytesReturnedByAllocSizeCall(Ctx, CE, Result); + std::optional<llvm::APInt> Size = + CE->evaluateBytesReturnedByAllocSizeCall(Ctx); + if (!Size) + return false; + + Result = std::move(*Size); + return true; } /// Attempts to evaluate the given LValueBase as the result of a call to @@ -10017,7 +10000,7 @@ bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) { if (ExprEvaluatorBaseTy::VisitCallExpr(E)) return true; - if (!(InvalidBaseOK && getAllocSizeAttr(E))) + if (!(InvalidBaseOK && E->getCalleeAllocSizeAttr())) return false; Result.setInvalid(E); @@ -10824,8 +10807,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, } bool RecordExprEvaluator::ZeroInitialization(const Expr *E, QualType T) { - const RecordDecl *RD = - T->castAs<RecordType>()->getOriginalDecl()->getDefinitionOrSelf(); + const auto *RD = T->castAsRecordDecl(); if (RD->isInvalidDecl()) return false; if (RD->isUnion()) { // C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the @@ -10894,10 +10876,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool RecordExprEvaluator::VisitCXXParenListOrInitListExpr( const Expr *ExprToVisit, ArrayRef<Expr *> Args) { - const RecordDecl *RD = ExprToVisit->getType() - ->castAs<RecordType>() - ->getOriginalDecl() - ->getDefinitionOrSelf(); + const auto *RD = ExprToVisit->getType()->castAsRecordDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); @@ -11121,10 +11100,7 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( Result = APValue(APValue::UninitStruct(), 0, 2); Array.moveInto(Result.getStructField(0)); - RecordDecl *Record = E->getType() - ->castAs<RecordType>() - ->getOriginalDecl() - ->getDefinitionOrSelf(); + auto *Record = E->getType()->castAsRecordDecl(); RecordDecl::field_iterator Field = Record->field_begin(); assert(Field != Record->field_end() && Info.Ctx.hasSameType(Field->getType()->getPointeeType(), @@ -11310,6 +11286,24 @@ static bool EvaluateVector(const Expr* E, APValue& Result, EvalInfo &Info) { return VectorExprEvaluator(Info, Result).Visit(E); } +static llvm::APInt ConvertBoolVectorToInt(const APValue &Val) { + assert(Val.isVector() && "expected vector APValue"); + unsigned NumElts = Val.getVectorLength(); + + // Each element is one bit, so create an integer with NumElts bits. + llvm::APInt Result(NumElts, 0); + + for (unsigned I = 0; I < NumElts; ++I) { + const APValue &Elt = Val.getVectorElt(I); + assert(Elt.isInt() && "expected integer element in bool vector"); + + if (Elt.getInt().getBoolValue()) + Result.setBit(I); + } + + return Result; +} + bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) { const VectorType *VTy = E->getType()->castAs<VectorType>(); unsigned NElts = VTy->getNumElements(); @@ -11603,6 +11597,38 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!IsConstantEvaluatedBuiltinCall(E)) return ExprEvaluatorBaseTy::VisitCallExpr(E); + auto EvaluateBinOpExpr = + [&](llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) { + APValue SourceLHS, SourceRHS; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) || + !EvaluateAsRValue(Info, E->getArg(1), SourceRHS)) + return false; + + auto *DestTy = E->getType()->castAs<VectorType>(); + QualType DestEltTy = DestTy->getElementType(); + bool DestUnsigned = DestEltTy->isUnsignedIntegerOrEnumerationType(); + unsigned SourceLen = SourceLHS.getVectorLength(); + SmallVector<APValue, 4> ResultElements; + ResultElements.reserve(SourceLen); + + if (SourceRHS.isInt()) { + const APSInt &RHS = SourceRHS.getInt(); + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + const APSInt &LHS = SourceLHS.getVectorElt(EltNum).getInt(); + ResultElements.push_back( + APValue(APSInt(Fn(LHS, RHS), DestUnsigned))); + } + } else { + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + const APSInt &LHS = SourceLHS.getVectorElt(EltNum).getInt(); + const APSInt &RHS = SourceRHS.getVectorElt(EltNum).getInt(); + ResultElements.push_back( + APValue(APSInt(Fn(LHS, RHS), DestUnsigned))); + } + } + return Success(APValue(ResultElements.data(), SourceLen), E); + }; + switch (E->getBuiltinCallee()) { default: return false; @@ -11659,99 +11685,108 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { } case Builtin::BI__builtin_elementwise_add_sat: + return EvaluateBinOpExpr([](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); + }); + case Builtin::BI__builtin_elementwise_sub_sat: + return EvaluateBinOpExpr([](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); + }); + + case clang::X86::BI__builtin_ia32_pavgb128: + case clang::X86::BI__builtin_ia32_pavgw128: + case clang::X86::BI__builtin_ia32_pavgb256: + case clang::X86::BI__builtin_ia32_pavgw256: + case clang::X86::BI__builtin_ia32_pavgb512: + case clang::X86::BI__builtin_ia32_pavgw512: + return EvaluateBinOpExpr(llvm::APIntOps::avgCeilU); + case clang::X86::BI__builtin_ia32_pmulhuw128: case clang::X86::BI__builtin_ia32_pmulhuw256: case clang::X86::BI__builtin_ia32_pmulhuw512: + return EvaluateBinOpExpr(llvm::APIntOps::mulhu); + case clang::X86::BI__builtin_ia32_pmulhw128: case clang::X86::BI__builtin_ia32_pmulhw256: case clang::X86::BI__builtin_ia32_pmulhw512: + return EvaluateBinOpExpr(llvm::APIntOps::mulhs); + case clang::X86::BI__builtin_ia32_psllv2di: case clang::X86::BI__builtin_ia32_psllv4di: case clang::X86::BI__builtin_ia32_psllv4si: + case clang::X86::BI__builtin_ia32_psllv8di: + case clang::X86::BI__builtin_ia32_psllv8hi: case clang::X86::BI__builtin_ia32_psllv8si: + case clang::X86::BI__builtin_ia32_psllv16hi: + case clang::X86::BI__builtin_ia32_psllv16si: + case clang::X86::BI__builtin_ia32_psllv32hi: + case clang::X86::BI__builtin_ia32_psllwi128: + case clang::X86::BI__builtin_ia32_pslldi128: + case clang::X86::BI__builtin_ia32_psllqi128: + case clang::X86::BI__builtin_ia32_psllwi256: + case clang::X86::BI__builtin_ia32_pslldi256: + case clang::X86::BI__builtin_ia32_psllqi256: + case clang::X86::BI__builtin_ia32_psllwi512: + case clang::X86::BI__builtin_ia32_pslldi512: + case clang::X86::BI__builtin_ia32_psllqi512: + return EvaluateBinOpExpr([](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); + } + return LHS.shl(RHS.getZExtValue()); + }); + case clang::X86::BI__builtin_ia32_psrav4si: + case clang::X86::BI__builtin_ia32_psrav8di: + case clang::X86::BI__builtin_ia32_psrav8hi: case clang::X86::BI__builtin_ia32_psrav8si: + case clang::X86::BI__builtin_ia32_psrav16hi: + case clang::X86::BI__builtin_ia32_psrav16si: + case clang::X86::BI__builtin_ia32_psrav32hi: + case clang::X86::BI__builtin_ia32_psravq128: + case clang::X86::BI__builtin_ia32_psravq256: + case clang::X86::BI__builtin_ia32_psrawi128: + case clang::X86::BI__builtin_ia32_psradi128: + case clang::X86::BI__builtin_ia32_psraqi128: + case clang::X86::BI__builtin_ia32_psrawi256: + case clang::X86::BI__builtin_ia32_psradi256: + case clang::X86::BI__builtin_ia32_psraqi256: + case clang::X86::BI__builtin_ia32_psrawi512: + case clang::X86::BI__builtin_ia32_psradi512: + case clang::X86::BI__builtin_ia32_psraqi512: + return EvaluateBinOpExpr([](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return LHS.ashr(LHS.getBitWidth() - 1); + } + return LHS.ashr(RHS.getZExtValue()); + }); + case clang::X86::BI__builtin_ia32_psrlv2di: case clang::X86::BI__builtin_ia32_psrlv4di: case clang::X86::BI__builtin_ia32_psrlv4si: - case clang::X86::BI__builtin_ia32_psrlv8si:{ - APValue SourceLHS, SourceRHS; - if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) || - !EvaluateAsRValue(Info, E->getArg(1), SourceRHS)) - return false; - - QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType(); - bool DestUnsigned = DestEltTy->isUnsignedIntegerOrEnumerationType(); - unsigned SourceLen = SourceLHS.getVectorLength(); - SmallVector<APValue, 4> ResultElements; - ResultElements.reserve(SourceLen); - - for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { - APSInt LHS = SourceLHS.getVectorElt(EltNum).getInt(); - APSInt RHS = SourceRHS.getVectorElt(EltNum).getInt(); - switch (E->getBuiltinCallee()) { - case Builtin::BI__builtin_elementwise_add_sat: - ResultElements.push_back(APValue( - APSInt(LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS), - DestUnsigned))); - break; - case Builtin::BI__builtin_elementwise_sub_sat: - ResultElements.push_back(APValue( - APSInt(LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS), - DestUnsigned))); - break; - case clang::X86::BI__builtin_ia32_pmulhuw128: - case clang::X86::BI__builtin_ia32_pmulhuw256: - case clang::X86::BI__builtin_ia32_pmulhuw512: - ResultElements.push_back(APValue(APSInt(llvm::APIntOps::mulhu(LHS, RHS), - /*isUnsigned=*/true))); - break; - case clang::X86::BI__builtin_ia32_pmulhw128: - case clang::X86::BI__builtin_ia32_pmulhw256: - case clang::X86::BI__builtin_ia32_pmulhw512: - ResultElements.push_back(APValue(APSInt(llvm::APIntOps::mulhs(LHS, RHS), - /*isUnsigned=*/false))); - break; - case clang::X86::BI__builtin_ia32_psllv2di: - case clang::X86::BI__builtin_ia32_psllv4di: - case clang::X86::BI__builtin_ia32_psllv4si: - case clang::X86::BI__builtin_ia32_psllv8si: - if (RHS.uge(RHS.getBitWidth())) { - ResultElements.push_back( - APValue(APSInt(APInt::getZero(RHS.getBitWidth()), DestUnsigned))); - break; - } - ResultElements.push_back( - APValue(APSInt(LHS.shl(RHS.getZExtValue()), DestUnsigned))); - break; - case clang::X86::BI__builtin_ia32_psrav4si: - case clang::X86::BI__builtin_ia32_psrav8si: - if (RHS.uge(RHS.getBitWidth())) { - ResultElements.push_back( - APValue(APSInt(LHS.ashr(RHS.getBitWidth() - 1), DestUnsigned))); - break; - } - ResultElements.push_back( - APValue(APSInt(LHS.ashr(RHS.getZExtValue()), DestUnsigned))); - break; - case clang::X86::BI__builtin_ia32_psrlv2di: - case clang::X86::BI__builtin_ia32_psrlv4di: - case clang::X86::BI__builtin_ia32_psrlv4si: - case clang::X86::BI__builtin_ia32_psrlv8si: - if (RHS.uge(RHS.getBitWidth())) { - ResultElements.push_back( - APValue(APSInt(APInt::getZero(RHS.getBitWidth()), DestUnsigned))); - break; - } - ResultElements.push_back( - APValue(APSInt(LHS.lshr(RHS.getZExtValue()), DestUnsigned))); - break; + case clang::X86::BI__builtin_ia32_psrlv8di: + case clang::X86::BI__builtin_ia32_psrlv8hi: + case clang::X86::BI__builtin_ia32_psrlv8si: + case clang::X86::BI__builtin_ia32_psrlv16hi: + case clang::X86::BI__builtin_ia32_psrlv16si: + case clang::X86::BI__builtin_ia32_psrlv32hi: + case clang::X86::BI__builtin_ia32_psrlwi128: + case clang::X86::BI__builtin_ia32_psrldi128: + case clang::X86::BI__builtin_ia32_psrlqi128: + case clang::X86::BI__builtin_ia32_psrlwi256: + case clang::X86::BI__builtin_ia32_psrldi256: + case clang::X86::BI__builtin_ia32_psrlqi256: + case clang::X86::BI__builtin_ia32_psrlwi512: + case clang::X86::BI__builtin_ia32_psrldi512: + case clang::X86::BI__builtin_ia32_psrlqi512: + return EvaluateBinOpExpr([](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); } - } + return LHS.lshr(RHS.getZExtValue()); + }); - return Success(APValue(ResultElements.data(), ResultElements.size()), E); - } case clang::X86::BI__builtin_ia32_pmuldq128: case clang::X86::BI__builtin_ia32_pmuldq256: case clang::X86::BI__builtin_ia32_pmuldq512: @@ -11789,6 +11824,29 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { return Success(APValue(ResultElements.data(), ResultElements.size()), E); } + + case clang::X86::BI__builtin_ia32_vprotbi: + case clang::X86::BI__builtin_ia32_vprotdi: + case clang::X86::BI__builtin_ia32_vprotqi: + case clang::X86::BI__builtin_ia32_vprotwi: + case clang::X86::BI__builtin_ia32_prold128: + case clang::X86::BI__builtin_ia32_prold256: + case clang::X86::BI__builtin_ia32_prold512: + case clang::X86::BI__builtin_ia32_prolq128: + case clang::X86::BI__builtin_ia32_prolq256: + case clang::X86::BI__builtin_ia32_prolq512: + return EvaluateBinOpExpr( + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotl(RHS); }); + + case clang::X86::BI__builtin_ia32_prord128: + case clang::X86::BI__builtin_ia32_prord256: + case clang::X86::BI__builtin_ia32_prord512: + case clang::X86::BI__builtin_ia32_prorq128: + case clang::X86::BI__builtin_ia32_prorq256: + case clang::X86::BI__builtin_ia32_prorq512: + return EvaluateBinOpExpr( + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); }); + case Builtin::BI__builtin_elementwise_max: case Builtin::BI__builtin_elementwise_min: { APValue SourceLHS, SourceRHS; @@ -11824,6 +11882,96 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { return Success(APValue(ResultElements.data(), ResultElements.size()), E); } + case X86::BI__builtin_ia32_vpshldd128: + case X86::BI__builtin_ia32_vpshldd256: + case X86::BI__builtin_ia32_vpshldd512: + case X86::BI__builtin_ia32_vpshldq128: + case X86::BI__builtin_ia32_vpshldq256: + case X86::BI__builtin_ia32_vpshldq512: + case X86::BI__builtin_ia32_vpshldw128: + case X86::BI__builtin_ia32_vpshldw256: + case X86::BI__builtin_ia32_vpshldw512: { + APValue SourceHi, SourceLo, SourceAmt; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) || + !EvaluateAsRValue(Info, E->getArg(1), SourceLo) || + !EvaluateAsRValue(Info, E->getArg(2), SourceAmt)) + return false; + + QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType(); + unsigned SourceLen = SourceHi.getVectorLength(); + SmallVector<APValue, 32> ResultElements; + ResultElements.reserve(SourceLen); + + APInt Amt = SourceAmt.getInt(); + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + APInt Hi = SourceHi.getVectorElt(EltNum).getInt(); + APInt Lo = SourceLo.getVectorElt(EltNum).getInt(); + APInt R = llvm::APIntOps::fshl(Hi, Lo, Amt); + ResultElements.push_back( + APValue(APSInt(R, DestEltTy->isUnsignedIntegerOrEnumerationType()))); + } + + return Success(APValue(ResultElements.data(), ResultElements.size()), E); + } + case X86::BI__builtin_ia32_vpshrdd128: + case X86::BI__builtin_ia32_vpshrdd256: + case X86::BI__builtin_ia32_vpshrdd512: + case X86::BI__builtin_ia32_vpshrdq128: + case X86::BI__builtin_ia32_vpshrdq256: + case X86::BI__builtin_ia32_vpshrdq512: + case X86::BI__builtin_ia32_vpshrdw128: + case X86::BI__builtin_ia32_vpshrdw256: + case X86::BI__builtin_ia32_vpshrdw512: { + // NOTE: Reversed Hi/Lo operands. + APValue SourceHi, SourceLo, SourceAmt; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceLo) || + !EvaluateAsRValue(Info, E->getArg(1), SourceHi) || + !EvaluateAsRValue(Info, E->getArg(2), SourceAmt)) + return false; + + QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType(); + unsigned SourceLen = SourceHi.getVectorLength(); + SmallVector<APValue, 32> ResultElements; + ResultElements.reserve(SourceLen); + + APInt Amt = SourceAmt.getInt(); + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + APInt Hi = SourceHi.getVectorElt(EltNum).getInt(); + APInt Lo = SourceLo.getVectorElt(EltNum).getInt(); + APInt R = llvm::APIntOps::fshr(Hi, Lo, Amt); + ResultElements.push_back( + APValue(APSInt(R, DestEltTy->isUnsignedIntegerOrEnumerationType()))); + } + + return Success(APValue(ResultElements.data(), ResultElements.size()), E); + } + case X86::BI__builtin_ia32_blendvpd: + case X86::BI__builtin_ia32_blendvpd256: + case X86::BI__builtin_ia32_blendvps: + case X86::BI__builtin_ia32_blendvps256: + case X86::BI__builtin_ia32_pblendvb128: + case X86::BI__builtin_ia32_pblendvb256: { + // SSE blendv by mask signbit: "Result = C[] < 0 ? T[] : F[]". + APValue SourceF, SourceT, SourceC; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceF) || + !EvaluateAsRValue(Info, E->getArg(1), SourceT) || + !EvaluateAsRValue(Info, E->getArg(2), SourceC)) + return false; + + unsigned SourceLen = SourceF.getVectorLength(); + SmallVector<APValue, 32> ResultElements; + ResultElements.reserve(SourceLen); + + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + const APValue &F = SourceF.getVectorElt(EltNum); + const APValue &T = SourceT.getVectorElt(EltNum); + const APValue &C = SourceC.getVectorElt(EltNum); + APInt M = C.isInt() ? (APInt)C.getInt() : C.getFloat().bitcastToAPInt(); + ResultElements.push_back(M.isNegative() ? T : F); + } + + return Success(APValue(ResultElements.data(), ResultElements.size()), E); + } case X86::BI__builtin_ia32_selectb_128: case X86::BI__builtin_ia32_selectb_256: case X86::BI__builtin_ia32_selectb_512: @@ -11937,6 +12085,40 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { } return Success(APValue(ResultElements.data(), ResultElements.size()), E); } + + case Builtin::BI__builtin_elementwise_fshl: + case Builtin::BI__builtin_elementwise_fshr: { + APValue SourceHi, SourceLo, SourceShift; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) || + !EvaluateAsRValue(Info, E->getArg(1), SourceLo) || + !EvaluateAsRValue(Info, E->getArg(2), SourceShift)) + return false; + + QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType(); + if (!DestEltTy->isIntegerType()) + return false; + + unsigned SourceLen = SourceHi.getVectorLength(); + SmallVector<APValue> ResultElements; + ResultElements.reserve(SourceLen); + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + const APSInt &Hi = SourceHi.getVectorElt(EltNum).getInt(); + const APSInt &Lo = SourceLo.getVectorElt(EltNum).getInt(); + const APSInt &Shift = SourceShift.getVectorElt(EltNum).getInt(); + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_elementwise_fshl: + ResultElements.push_back(APValue( + APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()))); + break; + case Builtin::BI__builtin_elementwise_fshr: + ResultElements.push_back(APValue( + APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()))); + break; + } + } + + return Success(APValue(ResultElements.data(), ResultElements.size()), E); + } } } @@ -13168,10 +13350,7 @@ static bool convertUnsignedAPIntToCharUnits(const llvm::APInt &Int, static void addFlexibleArrayMemberInitSize(EvalInfo &Info, const QualType &T, const LValue &LV, CharUnits &Size) { if (!T.isNull() && T->isStructureType() && - T->getAsStructureType() - ->getOriginalDecl() - ->getDefinitionOrSelf() - ->hasFlexibleArrayMember()) + T->castAsRecordDecl()->hasFlexibleArrayMember()) if (const auto *V = LV.getLValueBase().dyn_cast<const ValueDecl *>()) if (const auto *VD = dyn_cast<VarDecl>(V)) if (VD->hasInit()) @@ -13497,8 +13676,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__lzcnt: case Builtin::BI__lzcnt64: { APSInt Val; - if (!EvaluateInteger(E->getArg(0), Val, Info)) + if (E->getArg(0)->getType()->isExtVectorBoolType()) { + APValue Vec; + if (!EvaluateVector(E->getArg(0), Vec, Info)) + return false; + Val = ConvertBoolVectorToInt(Vec); + } else if (!EvaluateInteger(E->getArg(0), Val, Info)) { return false; + } std::optional<APSInt> Fallback; if ((BuiltinOp == Builtin::BI__builtin_clzg || @@ -13583,8 +13768,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_ctzg: case Builtin::BI__builtin_elementwise_cttz: { APSInt Val; - if (!EvaluateInteger(E->getArg(0), Val, Info)) + if (E->getArg(0)->getType()->isExtVectorBoolType()) { + APValue Vec; + if (!EvaluateVector(E->getArg(0), Vec, Info)) + return false; + Val = ConvertBoolVectorToInt(Vec); + } else if (!EvaluateInteger(E->getArg(0), Val, Info)) { return false; + } std::optional<APSInt> Fallback; if ((BuiltinOp == Builtin::BI__builtin_ctzg || @@ -13799,8 +13990,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__popcnt: case Builtin::BI__popcnt64: { APSInt Val; - if (!EvaluateInteger(E->getArg(0), Val, Info)) + if (E->getArg(0)->getType()->isExtVectorBoolType()) { + APValue Vec; + if (!EvaluateVector(E->getArg(0), Vec, Info)) + return false; + Val = ConvertBoolVectorToInt(Vec); + } else if (!EvaluateInteger(E->getArg(0), Val, Info)) { return false; + } return Success(Val.popcount(), E); } @@ -13875,6 +14072,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, APInt Result = std::min(LHS, RHS); return Success(APSInt(Result, !LHS.isSigned()), E); } + case Builtin::BI__builtin_elementwise_fshl: + case Builtin::BI__builtin_elementwise_fshr: { + APSInt Hi, Lo, Shift; + if (!EvaluateInteger(E->getArg(0), Hi, Info) || + !EvaluateInteger(E->getArg(1), Lo, Info) || + !EvaluateInteger(E->getArg(2), Shift, Info)) + return false; + + switch (BuiltinOp) { + case Builtin::BI__builtin_elementwise_fshl: { + APSInt Result(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()); + return Success(Result, E); + } + case Builtin::BI__builtin_elementwise_fshr: { + APSInt Result(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()); + return Success(Result, E); + } + } + } case Builtin::BIstrlen: case Builtin::BIwcslen: // A call to strlen is not a constant expression. @@ -14910,6 +15126,11 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // Reject differing bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { + // Bail out early if we're checking potential constant expression. + // Otherwise, prefer to diagnose other issues. + if (Info.checkingPotentialConstantExpression() && + (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown)) + return false; 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()); @@ -15230,6 +15451,10 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { // Reject differing bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { + if (Info.checkingPotentialConstantExpression() && + (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown)) + return false; + const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr *>(); const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>(); @@ -15408,6 +15633,13 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( const auto *VAT = Info.Ctx.getAsVariableArrayType(Ty); assert(VAT); if (VAT->getElementType()->isArrayType()) { + // Variable array size expression could be missing (e.g. int a[*][10]) In + // that case, it can't be a constant expression. + if (!VAT->getSizeExpr()) { + Info.FFDiag(E->getBeginLoc()); + return false; + } + std::optional<APSInt> Res = VAT->getSizeExpr()->getIntegerConstantExpr(Info.Ctx); if (Res) { @@ -15455,10 +15687,9 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { case OffsetOfNode::Field: { FieldDecl *MemberDecl = ON.getField(); - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) + const auto *RD = CurrentType->getAsRecordDecl(); + if (!RD) return Error(OOE); - RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); unsigned i = MemberDecl->getFieldIndex(); @@ -15477,22 +15708,20 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { return Error(OOE); // Find the layout of the class whose base we are looking into. - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) + const auto *RD = CurrentType->getAsCXXRecordDecl(); + if (!RD) return Error(OOE); - RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); // Find the base class itself. CurrentType = BaseSpec->getType(); - const RecordType *BaseRT = CurrentType->getAs<RecordType>(); - if (!BaseRT) + const auto *BaseRD = CurrentType->getAsCXXRecordDecl(); + if (!BaseRD) return Error(OOE); // Add the offset to the base. - Result += RL.getBaseClassOffset(cast<CXXRecordDecl>( - BaseRT->getOriginalDecl()->getDefinitionOrSelf())); + Result += RL.getBaseClassOffset(BaseRD); break; } } @@ -15670,8 +15899,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { } if (Info.Ctx.getLangOpts().CPlusPlus && DestType->isEnumeralType()) { - const EnumType *ET = dyn_cast<EnumType>(DestType.getCanonicalType()); - const EnumDecl *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + const auto *ED = DestType->getAsEnumDecl(); // Check that the value is within the range of the enumeration values. // // This corressponds to [expr.static.cast]p10 which says: @@ -17945,7 +18173,10 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { // it is an ICE or not. const auto *VAT = Ctx.getAsVariableArrayType(ArgTy); if (VAT->getElementType()->isArrayType()) - return CheckICE(VAT->getSizeExpr(), Ctx); + // Variable array size expression could be missing (e.g. int a[*][10]) + // In that case, it can't be a constant expression. + return VAT->getSizeExpr() ? CheckICE(VAT->getSizeExpr(), Ctx) + : ICEDiag(IK_NotICE, E->getBeginLoc()); // Otherwise, this is a regular VLA, which is definitely not an ICE. return ICEDiag(IK_NotICE, E->getBeginLoc()); diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 502a3e6b145e..d4cb89b43ae8 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -413,14 +413,13 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { return Match; case AnyCharTy: { - if (const auto *ETy = argTy->getAs<EnumType>()) { + if (const auto *ED = argTy->getAsEnumDecl()) { // If the enum is incomplete we know nothing about the underlying type. // Assume that it's 'int'. Do not use the underlying type for a scoped // enumeration. - const EnumDecl *ED = ETy->getOriginalDecl()->getDefinitionOrSelf(); if (!ED->isComplete()) return NoMatch; - if (ETy->isUnscopedEnumerationType()) + if (!ED->isScoped()) argTy = ED->getIntegerType(); } @@ -463,14 +462,13 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { return matchesSizeTPtrdiffT(C, argTy, T); } - if (const EnumType *ETy = argTy->getAs<EnumType>()) { + if (const auto *ED = argTy->getAsEnumDecl()) { // If the enum is incomplete we know nothing about the underlying type. // Assume that it's 'int'. Do not use the underlying type for a scoped // enumeration as that needs an exact match. - const EnumDecl *ED = ETy->getOriginalDecl()->getDefinitionOrSelf(); if (!ED->isComplete()) argTy = C.IntTy; - else if (ETy->isUnscopedEnumerationType()) + else if (!ED->isScoped()) argTy = ED->getIntegerType(); } @@ -655,7 +653,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { // to Objective-C objects. Since the compiler doesn't know which // structs can be toll-free bridged, we just accept them all. QualType pointee = PT->getPointeeType(); - if (pointee->getAsStructureType() || pointee->isVoidType()) + if (pointee->isStructureType() || pointee->isVoidType()) return Match; } return NoMatch; diff --git a/clang/lib/AST/InheritViz.cpp b/clang/lib/AST/InheritViz.cpp index c03492c64b16..3c4a5a8e2c4a 100644 --- a/clang/lib/AST/InheritViz.cpp +++ b/clang/lib/AST/InheritViz.cpp @@ -89,8 +89,8 @@ void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) { Out << " \"];\n"; // Display the base classes. - const auto *Decl = static_cast<const CXXRecordDecl *>( - Type->castAs<RecordType>()->getOriginalDecl()); + const auto *Decl = cast<CXXRecordDecl>( + Type->castAsCanonical<RecordType>()->getOriginalDecl()); for (const auto &Base : Decl->bases()) { QualType CanonBaseType = Context.getCanonicalType(Base.getType()); diff --git a/clang/lib/AST/ItaniumCXXABI.cpp b/clang/lib/AST/ItaniumCXXABI.cpp index 43a8bcd9443f..adef1584fd9b 100644 --- a/clang/lib/AST/ItaniumCXXABI.cpp +++ b/clang/lib/AST/ItaniumCXXABI.cpp @@ -42,8 +42,7 @@ namespace { /// /// Returns the name of anonymous union VarDecl or nullptr if it is not found. static const IdentifierInfo *findAnonymousUnionVarDeclName(const VarDecl& VD) { - const auto *RT = VD.getType()->castAs<RecordType>(); - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); + const auto *RD = VD.getType()->castAsRecordDecl(); assert(RD->isUnion() && "RecordType is expected to be a union."); if (const FieldDecl *FD = RD->findFirstNamedDataMember()) { return FD->getIdentifier(); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 112678fb2714..163cd43abd45 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1580,10 +1580,7 @@ void CXXNameMangler::mangleUnqualifiedName( if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { // We must have an anonymous union or struct declaration. - const RecordDecl *RD = VD->getType() - ->castAs<RecordType>() - ->getOriginalDecl() - ->getDefinitionOrSelf(); + const auto *RD = VD->getType()->castAsRecordDecl(); // Itanium C++ ABI 5.1.2: // @@ -4729,7 +4726,7 @@ void CXXNameMangler::mangleIntegerLiteral(QualType T, void CXXNameMangler::mangleMemberExprBase(const Expr *Base, bool IsArrow) { // Ignore member expressions involving anonymous unions. - while (const auto *RT = Base->getType()->getAs<RecordType>()) { + while (const auto *RT = Base->getType()->getAsCanonical<RecordType>()) { if (!RT->getOriginalDecl()->isAnonymousStructOrUnion()) break; const auto *ME = dyn_cast<MemberExpr>(Base); @@ -6029,6 +6026,8 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T, // ::= CI2 <type> # base inheriting constructor // // In addition, C5 is a comdat name with C1 and C2 in it. + // C4 represents a ctor declaration and is used by debuggers to look up + // the various ctor variants. Out << 'C'; if (InheritedFrom) Out << 'I'; @@ -6039,6 +6038,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T, case Ctor_Base: Out << '2'; break; + case Ctor_Unified: + Out << '4'; + break; case Ctor_Comdat: Out << '5'; break; @@ -6056,6 +6058,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { // ::= D2 # base object destructor // // In addition, D5 is a comdat name with D1, D2 and, if virtual, D0 in it. + // D4 represents a dtor declaration and is used by debuggers to look up + // the various dtor variants. switch (T) { case Dtor_Deleting: Out << "D0"; @@ -6066,6 +6070,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { case Dtor_Base: Out << "D2"; break; + case Dtor_Unified: + Out << "D4"; + break; case Dtor_Comdat: Out << "D5"; break; @@ -6997,8 +7004,8 @@ static bool hasMangledSubstitutionQualifiers(QualType T) { bool CXXNameMangler::mangleSubstitution(QualType T) { if (!hasMangledSubstitutionQualifiers(T)) { - if (const RecordType *RT = T->getAs<RecordType>()) - return mangleSubstitution(RT->getOriginalDecl()->getDefinitionOrSelf()); + if (const auto *RD = T->getAsCXXRecordDecl()) + return mangleSubstitution(RD); } uintptr_t TypePtr = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); @@ -7034,7 +7041,7 @@ bool CXXNameMangler::isSpecializedAs(QualType S, llvm::StringRef Name, if (S.isNull()) return false; - const RecordType *RT = S->getAs<RecordType>(); + const RecordType *RT = S->getAsCanonical<RecordType>(); if (!RT) return false; @@ -7168,8 +7175,8 @@ bool CXXNameMangler::mangleStandardSubstitution(const NamedDecl *ND) { void CXXNameMangler::addSubstitution(QualType T) { if (!hasMangledSubstitutionQualifiers(T)) { - if (const RecordType *RT = T->getAs<RecordType>()) { - addSubstitution(RT->getOriginalDecl()->getDefinitionOrSelf()); + if (const auto *RD = T->getAsCXXRecordDecl()) { + addSubstitution(RD); return; } } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index b3f12a1cce2e..2f4aebd0845d 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -396,7 +396,7 @@ llvm::json::Array JSONNodeDumper::createCastPath(const CastExpr *C) { for (auto I = C->path_begin(), E = C->path_end(); I != E; ++I) { const CXXBaseSpecifier *Base = *I; const auto *RD = cast<CXXRecordDecl>( - Base->getType()->castAs<RecordType>()->getOriginalDecl()); + Base->getType()->castAsCanonical<RecordType>()->getOriginalDecl()); llvm::json::Object Val{{"name", RD->getName()}}; if (Base->isVirtual()) @@ -1671,6 +1671,13 @@ void JSONNodeDumper::VisitLabelStmt(const LabelStmt *LS) { JOS.attribute("declId", createPointerRepresentation(LS->getDecl())); attributeOnlyIfTrue("sideEntry", LS->isSideEntry()); } + +void JSONNodeDumper::VisitLoopControlStmt(const LoopControlStmt *LS) { + if (LS->hasLabelTarget()) + JOS.attribute("targetLabelDeclId", + createPointerRepresentation(LS->getLabelDecl())); +} + void JSONNodeDumper::VisitGotoStmt(const GotoStmt *GS) { JOS.attribute("targetLabelDeclId", createPointerRepresentation(GS->getLabel())); diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 0bfb51c11f0a..780b2c585c81 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -152,6 +152,37 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { return shouldMangleCXXName(D); } +static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:"; + +/// Given an LLDB function call label, this function prints the label +/// into \c Out, together with the structor type of \c GD (if the +/// decl is a constructor/destructor). LLDB knows how to handle mangled +/// names with this encoding. +/// +/// Example input label: +/// $__lldb_func::123:456:~Foo +/// +/// Example output: +/// $__lldb_func:D1:123:456:~Foo +/// +static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD, + llvm::raw_ostream &Out) { + assert(label.starts_with(g_lldb_func_call_label_prefix)); + + Out << g_lldb_func_call_label_prefix; + + if (auto *Ctor = llvm::dyn_cast<clang::CXXConstructorDecl>(GD.getDecl())) { + Out << "C"; + if (Ctor->getInheritedConstructor().getConstructor()) + Out << "I"; + Out << GD.getCtorType(); + } else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl())) { + Out << "D" << GD.getDtorType(); + } + + Out << label.substr(g_lldb_func_call_label_prefix.size()); +} + void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { const ASTContext &ASTContext = getASTContext(); const NamedDecl *D = cast<NamedDecl>(GD.getDecl()); @@ -185,7 +216,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { if (!UserLabelPrefix.empty()) Out << '\01'; // LLVM IR Marker for __asm("foo") - Out << ALA->getLabel(); + if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix)) + emitLLDBAsmLabel(ALA->getLabel(), GD, Out); + else + Out << ALA->getLabel(); + return; } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index fc79ab1de24f..d96472e393f6 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1496,6 +1496,8 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) { // it. case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT"); + case Dtor_Unified: + llvm_unreachable("not expecting a unified dtor type"); } llvm_unreachable("Unsupported dtor type?"); } @@ -3246,13 +3248,17 @@ void MicrosoftCXXNameMangler::mangleTagTypeKind(TagTypeKind TTK) { } void MicrosoftCXXNameMangler::mangleType(const EnumType *T, Qualifiers, SourceRange) { - mangleType(cast<TagType>(T)->getOriginalDecl()->getDefinitionOrSelf()); + mangleType(cast<TagType>(T)->getOriginalDecl()); } void MicrosoftCXXNameMangler::mangleType(const RecordType *T, Qualifiers, SourceRange) { - mangleType(cast<TagType>(T)->getOriginalDecl()->getDefinitionOrSelf()); + mangleType(cast<TagType>(T)->getOriginalDecl()); } void MicrosoftCXXNameMangler::mangleType(const TagDecl *TD) { + // MSVC chooses the tag kind of the definition if it exists, otherwise it + // always picks the first declaration. + const auto *Def = TD->getDefinition(); + TD = Def ? Def : TD->getFirstDecl(); mangleTagTypeKind(TD->getTagKind()); mangleName(TD); } diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index 9a9ede467331..6c4bc7c274ea 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -317,11 +317,11 @@ OpenACCTileClause *OpenACCTileClause::Create(const ASTContext &C, OpenACCPrivateClause * OpenACCPrivateClause::Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef<Expr *> VarList, - ArrayRef<VarDecl *> InitRecipes, + ArrayRef<OpenACCPrivateRecipe> InitRecipes, SourceLocation EndLoc) { assert(VarList.size() == InitRecipes.size()); - void *Mem = - C.Allocate(OpenACCPrivateClause::totalSizeToAlloc<Expr *, VarDecl *>( + void *Mem = C.Allocate( + OpenACCPrivateClause::totalSizeToAlloc<Expr *, OpenACCPrivateRecipe>( VarList.size(), InitRecipes.size())); return new (Mem) OpenACCPrivateClause(BeginLoc, LParenLoc, VarList, InitRecipes, EndLoc); diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 588b0dcc6d7b..0930ca27c29f 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -104,6 +104,8 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { return static_cast<const OMPFilterClause *>(C); case OMPC_ompx_dyn_cgroup_mem: return static_cast<const OMPXDynCGroupMemClause *>(C); + case OMPC_message: + return static_cast<const OMPMessageClause *>(C); case OMPC_default: case OMPC_proc_bind: case OMPC_safelen: @@ -158,7 +160,6 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_self_maps: case OMPC_at: case OMPC_severity: - case OMPC_message: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -1963,8 +1964,10 @@ void OMPClausePrinter::VisitOMPSeverityClause(OMPSeverityClause *Node) { } void OMPClausePrinter::VisitOMPMessageClause(OMPMessageClause *Node) { - OS << "message(\"" - << cast<StringLiteral>(Node->getMessageString())->getString() << "\")"; + OS << "message("; + if (Expr *E = Node->getMessageString()) + E->printPretty(OS, nullptr, Policy); + OS << ")"; } void OMPClausePrinter::VisitOMPScheduleClause(OMPScheduleClause *Node) { diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index 687160c6116b..855550475721 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -793,8 +793,8 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, } // If it's an enum, get its underlying type. - if (const EnumType *ETy = QT->getAs<EnumType>()) - QT = ETy->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType(); + if (const auto *ED = QT->getAsEnumDecl()) + QT = ED->getIntegerType(); const BuiltinType *BT = QT->getAs<BuiltinType>(); if (!BT) { diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index f1f21f426f94..43f4e070748b 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -187,6 +187,7 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { // Check the bases. for (const CXXBaseSpecifier &Base : Class->bases()) { const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); + assert(BaseDecl != Class && "Class cannot inherit from itself."); CharUnits EmptySize; const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl); @@ -204,15 +205,13 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { // Check the fields. for (const FieldDecl *FD : Class->fields()) { - const RecordType *RT = - Context.getBaseElementType(FD->getType())->getAs<RecordType>(); - - // We only care about record types. - if (!RT) + // We only care about records. + const auto *MemberDecl = + Context.getBaseElementType(FD->getType())->getAsCXXRecordDecl(); + if (!MemberDecl) continue; CharUnits EmptySize; - const CXXRecordDecl *MemberDecl = RT->getAsCXXRecordDecl(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(MemberDecl); if (MemberDecl->isEmpty()) { // If the class decl is empty, get its size. @@ -433,11 +432,10 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const FieldDecl *FD, // If we have an array type we need to look at every element. if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { QualType ElemTy = Context.getBaseElementType(AT); - const RecordType *RT = ElemTy->getAs<RecordType>(); - if (!RT) + const auto *RD = ElemTy->getAsCXXRecordDecl(); + if (!RD) return true; - const CXXRecordDecl *RD = RT->getAsCXXRecordDecl(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); uint64_t NumElements = Context.getConstantArrayElementCount(AT); @@ -533,11 +531,10 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects( // If we have an array type we need to update every element. if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { QualType ElemTy = Context.getBaseElementType(AT); - const RecordType *RT = ElemTy->getAs<RecordType>(); - if (!RT) + const auto *RD = ElemTy->getAsCXXRecordDecl(); + if (!RD) return; - const CXXRecordDecl *RD = RT->getAsCXXRecordDecl(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); uint64_t NumElements = Context.getConstantArrayElementCount(AT); @@ -2011,7 +2008,7 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, CTy->getElementType()->castAs<BuiltinType>()); } else if (const BuiltinType *BTy = BaseTy->getAs<BuiltinType>()) { performBuiltinTypeAlignmentUpgrade(BTy); - } else if (const RecordType *RT = BaseTy->getAs<RecordType>()) { + } else if (const RecordType *RT = BaseTy->getAsCanonical<RecordType>()) { const RecordDecl *RD = RT->getOriginalDecl(); const ASTRecordLayout &FieldRecord = Context.getASTRecordLayout(RD); PreferredAlign = FieldRecord.getPreferredAlignment(); @@ -2711,8 +2708,9 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo( // alignment when it is applied to bitfields. Info.Alignment = std::max(Info.Alignment, FieldRequiredAlignment); else { - if (auto RT = - FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) { + if (const auto *RT = FD->getType() + ->getBaseElementTypeUnsafe() + ->getAsCanonical<RecordType>()) { auto const &Layout = Context.getASTRecordLayout(RT->getOriginalDecl()); EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); FieldRequiredAlignment = std::max(FieldRequiredAlignment, @@ -3695,9 +3693,9 @@ static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD, Offset + C.toCharUnitsFromBits(LocalFieldOffsetInBits); // Recursively dump fields of record type. - if (auto RT = Field->getType()->getAs<RecordType>()) { - DumpRecordLayout(OS, RT->getOriginalDecl()->getDefinitionOrSelf(), C, - FieldOffset, IndentLevel, Field->getName().data(), + if (const auto *RD = Field->getType()->getAsRecordDecl()) { + DumpRecordLayout(OS, RD, C, FieldOffset, IndentLevel, + Field->getName().data(), /*PrintSizeInfo=*/false, /*IncludeVirtualBases=*/true); continue; diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 31c001d025fe..41cf71a3e042 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -430,9 +430,8 @@ bool ScanfSpecifier::fixType(QualType QT, QualType RawQT, QualType PT = QT->getPointeeType(); // If it's an enum, get its underlying type. - if (const EnumType *ETy = PT->getAs<EnumType>()) { + if (const auto *ED = PT->getAsEnumDecl()) { // Don't try to fix incomplete enums. - const EnumDecl *ED = ETy->getOriginalDecl()->getDefinitionOrSelf(); if (!ED->isComplete()) return false; PT = ED->getIntegerType(); diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 4fc4a99ad240..9ae8aea3ab37 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -1482,3 +1482,16 @@ bool CapturedStmt::capturesVariable(const VarDecl *Var) const { return false; } + +const Stmt *LabelStmt::getInnermostLabeledStmt() const { + const Stmt *S = getSubStmt(); + while (isa_and_present<LabelStmt>(S)) + S = cast<LabelStmt>(S)->getSubStmt(); + return S; +} + +const Stmt *LoopControlStmt::getNamedLoopOrSwitch() const { + if (!hasLabelTarget()) + return nullptr; + return getLabelDecl()->getStmt()->getInnermostLabeledStmt(); +} diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index 2eeb5e45ab51..36ecaf6489ef 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -125,12 +125,13 @@ OMPLoopBasedDirective::tryToFindNextInnerLoop(Stmt *CurStmt, bool OMPLoopBasedDirective::doForAllLoops( Stmt *CurStmt, bool TryImperfectlyNestedLoops, unsigned NumLoops, llvm::function_ref<bool(unsigned, Stmt *)> Callback, - llvm::function_ref<void(OMPLoopTransformationDirective *)> + llvm::function_ref<void(OMPCanonicalLoopNestTransformationDirective *)> OnTransformationCallback) { CurStmt = CurStmt->IgnoreContainers(); for (unsigned Cnt = 0; Cnt < NumLoops; ++Cnt) { while (true) { - auto *Dir = dyn_cast<OMPLoopTransformationDirective>(CurStmt); + auto *Dir = + dyn_cast<OMPCanonicalLoopNestTransformationDirective>(CurStmt); if (!Dir) break; @@ -369,11 +370,11 @@ OMPForDirective *OMPForDirective::Create( return Dir; } -Stmt *OMPLoopTransformationDirective::getTransformedStmt() const { +Stmt *OMPCanonicalLoopNestTransformationDirective::getTransformedStmt() const { switch (getStmtClass()) { #define STMT(CLASS, PARENT) #define ABSTRACT_STMT(CLASS) -#define OMPLOOPTRANSFORMATIONDIRECTIVE(CLASS, PARENT) \ +#define OMPCANONICALLOOPNESTTRANSFORMATIONDIRECTIVE(CLASS, PARENT) \ case Stmt::CLASS##Class: \ return static_cast<const CLASS *>(this)->getTransformedStmt(); #include "clang/AST/StmtNodes.inc" @@ -382,11 +383,11 @@ Stmt *OMPLoopTransformationDirective::getTransformedStmt() const { } } -Stmt *OMPLoopTransformationDirective::getPreInits() const { +Stmt *OMPCanonicalLoopNestTransformationDirective::getPreInits() const { switch (getStmtClass()) { #define STMT(CLASS, PARENT) #define ABSTRACT_STMT(CLASS) -#define OMPLOOPTRANSFORMATIONDIRECTIVE(CLASS, PARENT) \ +#define OMPCANONICALLOOPNESTTRANSFORMATIONDIRECTIVE(CLASS, PARENT) \ case Stmt::CLASS##Class: \ return static_cast<const CLASS *>(this)->getPreInits(); #include "clang/AST/StmtNodes.inc" diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index afccba8778fd..003030052112 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -473,12 +473,21 @@ void StmtPrinter::VisitIndirectGotoStmt(IndirectGotoStmt *Node) { } void StmtPrinter::VisitContinueStmt(ContinueStmt *Node) { - Indent() << "continue;"; + Indent(); + if (Node->hasLabelTarget()) + OS << "continue " << Node->getLabelDecl()->getIdentifier()->getName() + << ';'; + else + OS << "continue;"; if (Policy.IncludeNewlines) OS << NL; } void StmtPrinter::VisitBreakStmt(BreakStmt *Node) { - Indent() << "break;"; + Indent(); + if (Node->hasLabelTarget()) + OS << "break " << Node->getLabelDecl()->getIdentifier()->getName() << ';'; + else + OS << "break;"; if (Policy.IncludeNewlines) OS << NL; } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 2035fa7635f2..37c4d43ec0b2 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -999,30 +999,30 @@ void StmtProfiler::VisitOMPSimdDirective(const OMPSimdDirective *S) { VisitOMPLoopDirective(S); } -void StmtProfiler::VisitOMPLoopTransformationDirective( - const OMPLoopTransformationDirective *S) { +void StmtProfiler::VisitOMPCanonicalLoopNestTransformationDirective( + const OMPCanonicalLoopNestTransformationDirective *S) { VisitOMPLoopBasedDirective(S); } void StmtProfiler::VisitOMPTileDirective(const OMPTileDirective *S) { - VisitOMPLoopTransformationDirective(S); + VisitOMPCanonicalLoopNestTransformationDirective(S); } void StmtProfiler::VisitOMPStripeDirective(const OMPStripeDirective *S) { - VisitOMPLoopTransformationDirective(S); + VisitOMPCanonicalLoopNestTransformationDirective(S); } void StmtProfiler::VisitOMPUnrollDirective(const OMPUnrollDirective *S) { - VisitOMPLoopTransformationDirective(S); + VisitOMPCanonicalLoopNestTransformationDirective(S); } void StmtProfiler::VisitOMPReverseDirective(const OMPReverseDirective *S) { - VisitOMPLoopTransformationDirective(S); + VisitOMPCanonicalLoopNestTransformationDirective(S); } void StmtProfiler::VisitOMPInterchangeDirective( const OMPInterchangeDirective *S) { - VisitOMPLoopTransformationDirective(S); + VisitOMPCanonicalLoopNestTransformationDirective(S); } void StmtProfiler::VisitOMPForDirective(const OMPForDirective *S) { @@ -2636,8 +2636,11 @@ void OpenACCClauseProfiler::VisitPrivateClause( const OpenACCPrivateClause &Clause) { VisitClauseWithVarList(Clause); - for (auto *VD : Clause.getInitRecipes()) - Profiler.VisitDecl(VD); + for (auto &Recipe : Clause.getInitRecipes()) { + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); + } } void OpenACCClauseProfiler::VisitFirstPrivateClause( @@ -2645,7 +2648,9 @@ void OpenACCClauseProfiler::VisitFirstPrivateClause( VisitClauseWithVarList(Clause); for (auto &Recipe : Clause.getInitRecipes()) { - Profiler.VisitDecl(Recipe.RecipeDecl); + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); Profiler.VisitDecl(Recipe.InitFromTemporary); } } @@ -2750,11 +2755,13 @@ void OpenACCClauseProfiler::VisitReductionClause( VisitClauseWithVarList(Clause); for (auto &Recipe : Clause.getRecipes()) { - Profiler.VisitDecl(Recipe.RecipeDecl); + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); // TODO: OpenACC: Make sure we remember to update this when we figure out // what we're adding for the operation recipe, in the meantime, a static // assert will make sure we don't add something. - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int *)); + static_assert(sizeof(OpenACCReductionRecipe) == 2 * sizeof(int *)); } } diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 76050ceeb35a..76f96fb8c5dc 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -56,8 +56,8 @@ static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out, const llvm::APSInt &Val = TemplArg.getAsIntegral(); if (Policy.UseEnumerators) { - if (const EnumType *ET = T->getAs<EnumType>()) { - for (const EnumConstantDecl *ECD : ET->getOriginalDecl()->enumerators()) { + if (const auto *ED = T->getAsEnumDecl()) { + for (const EnumConstantDecl *ECD : ED->enumerators()) { // In Sema::CheckTemplateArugment, enum template arguments value are // extended to the size of the integer underlying the enum type. This // may create a size difference between the enum value and template diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index c171516c38c1..f2cb15dbc43d 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -289,28 +289,23 @@ QualifiedTemplateName *TemplateName::getAsQualifiedTemplateName() const { return dyn_cast_if_present<QualifiedTemplateName *>(Storage); } -QualifiedTemplateName * -TemplateName::getAsAdjustedQualifiedTemplateName() const { - for (std::optional<TemplateName> Cur = *this; Cur; - Cur = Cur->desugar(/*IgnoreDeduced=*/true)) - if (QualifiedTemplateName *N = Cur->getAsQualifiedTemplateName()) - return N; - return nullptr; -} - DependentTemplateName *TemplateName::getAsDependentTemplateName() const { return Storage.dyn_cast<DependentTemplateName *>(); } -NestedNameSpecifier TemplateName::getQualifier() const { +std::tuple<NestedNameSpecifier, bool> +TemplateName::getQualifierAndTemplateKeyword() const { for (std::optional<TemplateName> Cur = *this; Cur; Cur = Cur->desugar(/*IgnoreDeduced=*/true)) { if (DependentTemplateName *N = Cur->getAsDependentTemplateName()) - return N->getQualifier(); + return {N->getQualifier(), N->hasTemplateKeyword()}; if (QualifiedTemplateName *N = Cur->getAsQualifiedTemplateName()) - return N->getQualifier(); + return {N->getQualifier(), N->hasTemplateKeyword()}; + if (Cur->getAsSubstTemplateTemplateParm() || + Cur->getAsSubstTemplateTemplateParmPack()) + break; } - return std::nullopt; + return {std::nullopt, false}; } UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const { @@ -448,8 +443,14 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, Template = cast<TemplateDecl>(Template->getCanonicalDecl()); if (handleAnonymousTTP(Template, OS)) return; - if (Qual == Qualified::None || Policy.SuppressScope) { - OS << *Template; + if (Qual == Qualified::None || isa<TemplateTemplateParmDecl>(Template) || + Policy.SuppressScope) { + if (IdentifierInfo *II = Template->getIdentifier(); + Policy.CleanUglifiedParameters && II && + isa<TemplateTemplateParmDecl>(Template)) + OS << II->deuglifiedName(); + else + OS << *Template; } else { PrintingPolicy NestedNamePolicy = Policy; NestedNamePolicy.SuppressUnwrittenScope = true; @@ -474,12 +475,7 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, if (handleAnonymousTTP(UTD, OS)) return; - if (IdentifierInfo *II = UTD->getIdentifier(); - Policy.CleanUglifiedParameters && II && - isa<TemplateTemplateParmDecl>(UTD)) - OS << II->deuglifiedName(); - else - OS << *UTD; + OS << *UTD; } else if (DependentTemplateName *DTN = getAsDependentTemplateName()) { DTN->print(OS, Policy); } else if (SubstTemplateTemplateParmStorage *subst = diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 085616049373..8c59dbd34543 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1401,7 +1401,7 @@ static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) { OS << " -> "; const auto *RD = cast<CXXRecordDecl>( - Base->getType()->castAs<RecordType>()->getOriginalDecl()); + Base->getType()->castAsCanonical<RecordType>()->getOriginalDecl()); if (Base->isVirtual()) OS << "virtual "; @@ -1412,6 +1412,26 @@ static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) { OS << ')'; } +void TextNodeDumper::VisitLoopControlStmt(const LoopControlStmt *Node) { + if (!Node->hasLabelTarget()) + return; + + OS << " '" << Node->getLabelDecl()->getIdentifier()->getName() << "' ("; + + auto *Target = Node->getNamedLoopOrSwitch(); + if (!Target) { + ColorScope Color(OS, ShowColors, NullColor); + OS << "<<<NULL>>>"; + } else { + { + ColorScope Color(OS, ShowColors, StmtColor); + OS << Target->getStmtClassName(); + } + dumpPointer(Target); + } + OS << ")"; +} + void TextNodeDumper::VisitIfStmt(const IfStmt *Node) { if (Node->hasInitStorage()) OS << " has_init"; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index a70bc5424009..86621795d81e 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -113,10 +113,8 @@ const IdentifierInfo *QualType::getBaseTypeIdentifier() const { return DNT->getIdentifier(); if (ty->isPointerOrReferenceType()) return ty->getPointeeType().getBaseTypeIdentifier(); - if (ty->isRecordType()) - ND = ty->castAs<RecordType>()->getOriginalDecl(); - else if (ty->isEnumeralType()) - ND = ty->castAs<EnumType>()->getOriginalDecl(); + if (const auto *TT = ty->getAs<TagType>()) + ND = TT->getOriginalDecl(); else if (ty->getTypeClass() == Type::Typedef) ND = ty->castAs<TypedefType>()->getDecl(); else if (ty->isArrayType()) @@ -672,63 +670,59 @@ const Type *Type::getUnqualifiedDesugaredType() const { } bool Type::isClassType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl()->isClass(); return false; } bool Type::isStructureType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl()->isStruct(); return false; } bool Type::isStructureTypeWithFlexibleArrayMember() const { - const auto *RT = getAs<RecordType>(); + const auto *RT = getAsCanonical<RecordType>(); if (!RT) return false; - const auto *Decl = RT->getOriginalDecl()->getDefinitionOrSelf(); + const auto *Decl = RT->getOriginalDecl(); if (!Decl->isStruct()) return false; - return Decl->hasFlexibleArrayMember(); + return Decl->getDefinitionOrSelf()->hasFlexibleArrayMember(); } bool Type::isObjCBoxableRecordType() const { - if (const auto *RT = getAs<RecordType>()) - return RT->getOriginalDecl() - ->getDefinitionOrSelf() - ->hasAttr<ObjCBoxableAttr>(); + if (const auto *RD = getAsRecordDecl()) + return RD->hasAttr<ObjCBoxableAttr>(); return false; } bool Type::isInterfaceType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl()->isInterface(); return false; } bool Type::isStructureOrClassType() const { - if (const auto *RT = getAs<RecordType>()) { - RecordDecl *RD = RT->getOriginalDecl(); - return RD->isStruct() || RD->isClass() || RD->isInterface(); - } + if (const auto *RT = getAsCanonical<RecordType>()) + return RT->getOriginalDecl()->isStructureOrClass(); return false; } bool Type::isVoidPointerType() const { - if (const auto *PT = getAs<PointerType>()) + if (const auto *PT = getAsCanonical<PointerType>()) return PT->getPointeeType()->isVoidType(); return false; } bool Type::isUnionType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl()->isUnion(); return false; } bool Type::isComplexType() const { - if (const auto *CT = dyn_cast<ComplexType>(CanonicalType)) + if (const auto *CT = getAsCanonical<ComplexType>()) return CT->getElementType()->isFloatingType(); return false; } @@ -739,7 +733,7 @@ bool Type::isComplexIntegerType() const { } bool Type::isScopedEnumeralType() const { - if (const auto *ET = getAs<EnumType>()) + if (const auto *ET = getAsCanonical<EnumType>()) return ET->getOriginalDecl()->isScoped(); return false; } @@ -1914,41 +1908,13 @@ const ObjCObjectPointerType *Type::getAsObjCInterfacePointerType() const { const CXXRecordDecl *Type::getPointeeCXXRecordDecl() const { QualType PointeeType; - if (const auto *PT = getAs<PointerType>()) + if (const auto *PT = getAsCanonical<PointerType>()) PointeeType = PT->getPointeeType(); - else if (const auto *RT = getAs<ReferenceType>()) + else if (const auto *RT = getAsCanonical<ReferenceType>()) PointeeType = RT->getPointeeType(); else return nullptr; - - if (const auto *RT = PointeeType->getAs<RecordType>()) - return dyn_cast<CXXRecordDecl>( - RT->getOriginalDecl()->getDefinitionOrSelf()); - - return nullptr; -} - -CXXRecordDecl *Type::getAsCXXRecordDecl() const { - const auto *TT = dyn_cast<TagType>(CanonicalType); - if (!isa_and_present<RecordType, InjectedClassNameType>(TT)) - return nullptr; - auto *TD = TT->getOriginalDecl(); - if (!isa<InjectedClassNameType>(TT) && !isa<CXXRecordDecl>(TD)) - return nullptr; - return cast<CXXRecordDecl>(TD)->getDefinitionOrSelf(); -} - -RecordDecl *Type::getAsRecordDecl() const { - const auto *TT = dyn_cast<TagType>(CanonicalType); - if (!isa_and_present<RecordType, InjectedClassNameType>(TT)) - return nullptr; - return cast<RecordDecl>(TT->getOriginalDecl())->getDefinitionOrSelf(); -} - -TagDecl *Type::getAsTagDecl() const { - if (const auto *TT = dyn_cast<TagType>(CanonicalType)) - return TT->getOriginalDecl()->getDefinitionOrSelf(); - return nullptr; + return PointeeType->getAsCXXRecordDecl(); } const TemplateSpecializationType * @@ -1963,12 +1929,10 @@ NestedNameSpecifier Type::getPrefix() const { switch (getTypeClass()) { case Type::DependentName: return cast<DependentNameType>(this)->getQualifier(); - case Type::TemplateSpecialization: { - QualifiedTemplateName *S = cast<TemplateSpecializationType>(this) - ->getTemplateName() - .getAsAdjustedQualifiedTemplateName(); - return S ? S->getQualifier() : std::nullopt; - } + case Type::TemplateSpecialization: + return cast<TemplateSpecializationType>(this) + ->getTemplateName() + .getQualifier(); case Type::DependentTemplateSpecialization: return cast<DependentTemplateSpecializationType>(this) ->getDependentTemplateName() @@ -2246,10 +2210,9 @@ bool Type::isSignedIntegerType() const { if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) return BT->isSignedInteger(); - if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) { + if (const auto *ED = getAsEnumDecl()) { // Incomplete enum types are not treated as integer types. // FIXME: In C++, enum types are never integer types. - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); if (!ED->isComplete() || ED->isScoped()) return false; return ED->getIntegerType()->isSignedIntegerType(); @@ -2267,8 +2230,7 @@ bool Type::isSignedIntegerOrEnumerationType() const { if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) return BT->isSignedInteger(); - if (const auto *ET = dyn_cast<EnumType>(CanonicalType)) { - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *ED = getAsEnumDecl()) { if (!ED->isComplete()) return false; return ED->getIntegerType()->isSignedIntegerType(); @@ -2296,10 +2258,9 @@ bool Type::isUnsignedIntegerType() const { if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) return BT->isUnsignedInteger(); - if (const auto *ET = dyn_cast<EnumType>(CanonicalType)) { + if (const auto *ED = getAsEnumDecl()) { // Incomplete enum types are not treated as integer types. // FIXME: In C++, enum types are never integer types. - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); if (!ED->isComplete() || ED->isScoped()) return false; return ED->getIntegerType()->isUnsignedIntegerType(); @@ -2317,8 +2278,7 @@ bool Type::isUnsignedIntegerOrEnumerationType() const { if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) return BT->isUnsignedInteger(); - if (const auto *ET = dyn_cast<EnumType>(CanonicalType)) { - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *ED = getAsEnumDecl()) { if (!ED->isComplete()) return false; return ED->getIntegerType()->isUnsignedIntegerType(); @@ -2398,10 +2358,8 @@ bool Type::isArithmeticType() const { bool Type::hasBooleanRepresentation() const { if (const auto *VT = dyn_cast<VectorType>(CanonicalType)) return VT->getElementType()->isBooleanType(); - if (const auto *ET = dyn_cast<EnumType>(CanonicalType)) { - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *ED = getAsEnumDecl()) return ED->isComplete() && ED->getIntegerType()->isBooleanType(); - } if (const auto *IT = dyn_cast<BitIntType>(CanonicalType)) return IT->getNumBits() == 1; return isBooleanType(); @@ -2432,10 +2390,7 @@ Type::ScalarTypeKind Type::getScalarTypeKind() const { } else if (isa<MemberPointerType>(T)) { return STK_MemberPointer; } else if (isa<EnumType>(T)) { - assert(cast<EnumType>(T) - ->getOriginalDecl() - ->getDefinitionOrSelf() - ->isComplete()); + assert(T->castAsEnumDecl()->isComplete()); return STK_Integral; } else if (const auto *CT = dyn_cast<ComplexType>(T)) { if (CT->getElementType()->isRealFloatingType()) @@ -2459,8 +2414,8 @@ Type::ScalarTypeKind Type::getScalarTypeKind() const { /// includes union types. bool Type::isAggregateType() const { if (const auto *Record = dyn_cast<RecordType>(CanonicalType)) { - if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>( - Record->getOriginalDecl()->getDefinitionOrSelf())) + if (const auto *ClassDecl = + dyn_cast<CXXRecordDecl>(Record->getOriginalDecl())) return ClassDecl->isAggregate(); return true; @@ -2494,8 +2449,7 @@ bool Type::isIncompleteType(NamedDecl **Def) const { // be completed. return isVoidType(); case Enum: { - EnumDecl *EnumD = - cast<EnumType>(CanonicalType)->getOriginalDecl()->getDefinitionOrSelf(); + auto *EnumD = castAsEnumDecl(); if (Def) *Def = EnumD; return !EnumD->isComplete(); @@ -2503,17 +2457,13 @@ bool Type::isIncompleteType(NamedDecl **Def) const { case Record: { // A tagged type (struct/union/enum/class) is incomplete if the decl is a // forward declaration, but not a full definition (C99 6.2.5p22). - RecordDecl *Rec = cast<RecordType>(CanonicalType) - ->getOriginalDecl() - ->getDefinitionOrSelf(); + auto *Rec = castAsRecordDecl(); if (Def) *Def = Rec; return !Rec->isCompleteDefinition(); } case InjectedClassName: { - CXXRecordDecl *Rec = cast<InjectedClassNameType>(CanonicalType) - ->getOriginalDecl() - ->getDefinitionOrSelf(); + auto *Rec = castAsCXXRecordDecl(); if (!Rec->isBeingDefined()) return false; if (Def) @@ -2573,7 +2523,7 @@ bool Type::isAlwaysIncompleteType() const { // Forward declarations of structs, classes, enums, and unions could be later // completed in a compilation unit by providing a type definition. - if (getAsTagDecl()) + if (isa<TagType>(CanonicalType)) return false; // Other types are incompletable. @@ -2771,6 +2721,11 @@ bool QualType::isCXX98PODType(const ASTContext &Context) const { return false; QualType CanonicalType = getTypePtr()->CanonicalType; + + // Any type that is, or contains, address discriminated data is never POD. + if (Context.containsAddressDiscriminatedPointerAuth(CanonicalType)) + return false; + switch (CanonicalType->getTypeClass()) { // Everything not explicitly mentioned is not POD. default: @@ -2797,7 +2752,7 @@ bool QualType::isCXX98PODType(const ASTContext &Context) const { case Type::Record: if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>( cast<RecordType>(CanonicalType)->getOriginalDecl())) - return ClassDecl->getDefinitionOrSelf()->isPOD(); + return ClassDecl->isPOD(); // C struct/union is POD. return true; @@ -2829,6 +2784,11 @@ bool QualType::isTrivialType(const ASTContext &Context) const { if (CanonicalType->isDependentType()) return false; + // Any type that is, or contains, address discriminated data is never a + // trivial type. + if (Context.containsAddressDiscriminatedPointerAuth(CanonicalType)) + return false; + // C++0x [basic.types]p9: // Scalar types, trivial class types, arrays of such types, and // cv-qualified versions of these types are collectively called trivial @@ -2837,23 +2797,22 @@ bool QualType::isTrivialType(const ASTContext &Context) const { // As an extension, Clang treats vector types as Scalar types. if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) return true; - if (const auto *RT = CanonicalType->getAs<RecordType>()) { - if (const auto *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getOriginalDecl())) { - // C++20 [class]p6: - // A trivial class is a class that is trivially copyable, and - // has one or more eligible default constructors such that each is - // trivial. - // FIXME: We should merge this definition of triviality into - // CXXRecordDecl::isTrivial. Currently it computes the wrong thing. - return ClassDecl->hasTrivialDefaultConstructor() && - !ClassDecl->hasNonTrivialDefaultConstructor() && - ClassDecl->isTriviallyCopyable(); - } - return true; + if (const auto *ClassDecl = CanonicalType->getAsCXXRecordDecl()) { + // C++20 [class]p6: + // A trivial class is a class that is trivially copyable, and + // has one or more eligible default constructors such that each is + // trivial. + // FIXME: We should merge this definition of triviality into + // CXXRecordDecl::isTrivial. Currently it computes the wrong thing. + return ClassDecl->hasTrivialDefaultConstructor() && + !ClassDecl->hasNonTrivialDefaultConstructor() && + ClassDecl->isTriviallyCopyable(); } + if (isa<RecordType>(CanonicalType)) + return true; + // No other types can match. return false; } @@ -2897,18 +2856,13 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type, if (CanonicalType->isMFloat8Type()) return true; - if (const auto *RT = CanonicalType->getAs<RecordType>()) { - if (const auto *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getOriginalDecl())) { - if (IsCopyConstructible) { + if (const auto *RD = CanonicalType->getAsRecordDecl()) { + if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) { + if (IsCopyConstructible) return ClassDecl->isTriviallyCopyConstructible(); - } else { - return ClassDecl->isTriviallyCopyable(); - } + return ClassDecl->isTriviallyCopyable(); } - return !RT->getOriginalDecl() - ->getDefinitionOrSelf() - ->isNonTrivialToPrimitiveCopy(); + return !RD->isNonTrivialToPrimitiveCopy(); } // No other types can match. return false; @@ -2930,6 +2884,12 @@ bool QualType::isBitwiseCloneableType(const ASTContext &Context) const { if (CanonicalType->isIncompleteType()) return false; + + // Any type that is, or contains, address discriminated data is never + // bitwise clonable. + if (Context.containsAddressDiscriminatedPointerAuth(CanonicalType)) + return false; + const auto *RD = CanonicalType->getAsRecordDecl(); // struct/union/class if (!RD) return true; @@ -2996,11 +2956,9 @@ bool QualType::isWebAssemblyFuncrefType() const { QualType::PrimitiveDefaultInitializeKind QualType::isNonTrivialToPrimitiveDefaultInitialize() const { - if (const auto *RT = - getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>()) - if (RT->getOriginalDecl() - ->getDefinitionOrSelf() - ->isNonTrivialToPrimitiveDefaultInitialize()) + if (const auto *RD = + getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) + if (RD->isNonTrivialToPrimitiveDefaultInitialize()) return PDIK_Struct; switch (getQualifiers().getObjCLifetime()) { @@ -3014,11 +2972,9 @@ QualType::isNonTrivialToPrimitiveDefaultInitialize() const { } QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { - if (const auto *RT = - getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>()) - if (RT->getOriginalDecl() - ->getDefinitionOrSelf() - ->isNonTrivialToPrimitiveCopy()) + if (const auto *RD = + getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) + if (RD->isNonTrivialToPrimitiveCopy()) return PCK_Struct; Qualifiers Qs = getQualifiers(); @@ -3075,7 +3031,7 @@ bool Type::isLiteralType(const ASTContext &Ctx) const { if (BaseTy->isReferenceType()) return true; // -- a class type that has all of the following properties: - if (const auto *RT = BaseTy->getAs<RecordType>()) { + if (const auto *RD = BaseTy->getAsRecordDecl()) { // -- a trivial destructor, // -- every constructor call and full-expression in the // brace-or-equal-initializers for non-static data members (if any) @@ -3086,8 +3042,8 @@ bool Type::isLiteralType(const ASTContext &Ctx) const { // -- all non-static data members and base classes of literal types // // We resolve DR1361 by ignoring the second bullet. - if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getOriginalDecl())) - return ClassDecl->getDefinitionOrSelf()->isLiteral(); + if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) + return ClassDecl->isLiteral(); return true; } @@ -3139,10 +3095,10 @@ bool Type::isStandardLayoutType() const { // As an extension, Clang treats vector types as Scalar types. if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; - if (const auto *RT = BaseTy->getAs<RecordType>()) { - if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getOriginalDecl())) - if (!ClassDecl->getDefinitionOrSelf()->isStandardLayout()) - return false; + if (const auto *RD = BaseTy->getAsRecordDecl()) { + if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD); + ClassDecl && !ClassDecl->isStandardLayout()) + return false; // Default to 'true' for non-C++ class types. // FIXME: This is a bit dubious, but plain C structs should trivially meet @@ -3179,13 +3135,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const { if (BaseTy->isIncompleteType()) return false; + // Any type that is, or contains, address discriminated data is non-POD. + if (Context.containsAddressDiscriminatedPointerAuth(*this)) + return false; + // As an extension, Clang treats vector types as Scalar types. if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; - if (const auto *RT = BaseTy->getAs<RecordType>()) { - if (const auto *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getOriginalDecl())) { - ClassDecl = ClassDecl->getDefinitionOrSelf(); + if (const auto *RD = BaseTy->getAsRecordDecl()) { + if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) { // C++11 [class]p10: // A POD struct is a non-union class that is both a trivial class [...] if (!ClassDecl->isTrivial()) @@ -3224,7 +3182,7 @@ bool Type::isNothrowT() const { } bool Type::isAlignValT() const { - if (const auto *ET = getAs<EnumType>()) { + if (const auto *ET = getAsCanonical<EnumType>()) { const auto *ED = ET->getOriginalDecl(); IdentifierInfo *II = ED->getIdentifier(); if (II && II->isStr("align_val_t") && ED->isInStdNamespace()) @@ -3234,7 +3192,7 @@ bool Type::isAlignValT() const { } bool Type::isStdByteType() const { - if (const auto *ET = getAs<EnumType>()) { + if (const auto *ET = getAsCanonical<EnumType>()) { const auto *ED = ET->getOriginalDecl(); IdentifierInfo *II = ED->getIdentifier(); if (II && II->isStr("byte") && ED->isInStdNamespace()) @@ -4405,7 +4363,7 @@ bool RecordType::hasConstFields() const { if (FieldTy.isConstQualified()) return true; FieldTy = FieldTy.getCanonicalType(); - if (const auto *FieldRecTy = FieldTy->getAs<RecordType>()) { + if (const auto *FieldRecTy = FieldTy->getAsCanonical<RecordType>()) { if (!llvm::is_contained(RecordTypeList, FieldRecTy)) RecordTypeList.push_back(FieldRecTy); } @@ -5409,7 +5367,7 @@ bool Type::isObjCARCBridgableType() const { /// Determine whether the given type T is a "bridgeable" C type. bool Type::isCARCBridgableType() const { - const auto *Pointer = getAs<PointerType>(); + const auto *Pointer = getAsCanonical<PointerType>(); if (!Pointer) return false; @@ -5419,7 +5377,7 @@ bool Type::isCARCBridgableType() const { /// Check if the specified type is the CUDA device builtin surface type. bool Type::isCUDADeviceBuiltinSurfaceType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl() ->getMostRecentDecl() ->hasAttr<CUDADeviceBuiltinSurfaceTypeAttr>(); @@ -5428,7 +5386,7 @@ bool Type::isCUDADeviceBuiltinSurfaceType() const { /// Check if the specified type is the CUDA device builtin texture type. bool Type::isCUDADeviceBuiltinTextureType() const { - if (const auto *RT = getAs<RecordType>()) + if (const auto *RT = getAsCanonical<RecordType>()) return RT->getOriginalDecl() ->getMostRecentDecl() ->hasAttr<CUDADeviceBuiltinTextureTypeAttr>(); @@ -5462,7 +5420,7 @@ bool Type::isHLSLResourceRecordArray() const { const Type *Ty = getUnqualifiedDesugaredType(); if (!Ty->isArrayType()) return false; - while (isa<ConstantArrayType>(Ty)) + while (isa<ArrayType>(Ty)) Ty = Ty->getArrayElementTypeNoTypeQual(); return Ty->isHLSLResourceRecord(); } @@ -5475,7 +5433,7 @@ bool Type::isHLSLIntangibleType() const { return Ty->isHLSLBuiltinIntangibleType(); // unwrap arrays - while (isa<ConstantArrayType>(Ty)) + while (isa<ArrayType>(Ty)) Ty = Ty->getArrayElementTypeNoTypeQual(); const RecordType *RT = @@ -5503,8 +5461,7 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { return DK_objc_weak_lifetime; } - if (const auto *RT = type->getBaseElementTypeUnsafe()->getAs<RecordType>()) { - const RecordDecl *RD = RT->getOriginalDecl(); + if (const auto *RD = type->getBaseElementTypeUnsafe()->getAsRecordDecl()) { if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { /// Check if this is a C++ object with a non-trivial destructor. if (CXXRD->hasDefinition() && !CXXRD->hasTrivialDestructor()) @@ -5512,7 +5469,7 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { } else { /// Check if this is a C struct that is non-trivial to destroy or an array /// that contains such a struct. - if (RD->getDefinitionOrSelf()->isNonTrivialToPrimitiveDestroy()) + if (RD->isNonTrivialToPrimitiveDestroy()) return DK_nontrivial_c_struct; } } diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index fbe877292446..3e9597fc4d47 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -750,8 +750,9 @@ void TemplateSpecializationTypeLoc::set(SourceLocation ElaboratedKeywordLoc, void TemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { - QualifiedTemplateName *Name = - getTypePtr()->getTemplateName().getAsAdjustedQualifiedTemplateName(); + + auto [Qualifier, HasTemplateKeyword] = + getTypePtr()->getTemplateName().getQualifierAndTemplateKeyword(); SourceLocation ElaboratedKeywordLoc = getTypePtr()->getKeyword() != ElaboratedTypeKeyword::None @@ -759,8 +760,7 @@ void TemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context, : SourceLocation(); NestedNameSpecifierLoc QualifierLoc; - if (NestedNameSpecifier Qualifier = - Name ? Name->getQualifier() : std::nullopt) { + if (Qualifier) { NestedNameSpecifierLocBuilder Builder; Builder.MakeTrivial(Context, Qualifier, Loc); QualifierLoc = Builder.getWithLocInContext(Context); @@ -768,9 +768,7 @@ void TemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context, TemplateArgumentListInfo TAL(Loc, Loc); set(ElaboratedKeywordLoc, QualifierLoc, - /*TemplateKeywordLoc=*/Name && Name->hasTemplateKeyword() - ? Loc - : SourceLocation(), + /*TemplateKeywordLoc=*/HasTemplateKeyword ? Loc : SourceLocation(), /*NameLoc=*/Loc, /*LAngleLoc=*/Loc, /*RAngleLoc=*/Loc); initializeArgLocs(Context, getTypePtr()->template_arguments(), getArgInfos(), Loc); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 52ff0d5b5771..54ca42d2035a 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2382,7 +2382,7 @@ static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern, return true; // A type parameter matches its argument. - if (auto *TTPT = Pattern->getAs<TemplateTypeParmType>()) { + if (auto *TTPT = Pattern->getAsCanonical<TemplateTypeParmType>()) { if (TTPT->getDepth() == Depth && TTPT->getIndex() < Args.size() && Args[TTPT->getIndex()].getKind() == TemplateArgument::Type) { QualType SubstArg = Ctx.getQualifiedType( diff --git a/clang/lib/AST/VTTBuilder.cpp b/clang/lib/AST/VTTBuilder.cpp index 85101aee97e6..89b58b557ddc 100644 --- a/clang/lib/AST/VTTBuilder.cpp +++ b/clang/lib/AST/VTTBuilder.cpp @@ -63,11 +63,7 @@ void VTTBuilder::LayoutSecondaryVTTs(BaseSubobject Base) { if (I.isVirtual()) continue; - const auto *BaseDecl = - cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); - + const auto *BaseDecl = I.getType()->castAsCXXRecordDecl(); const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); CharUnits BaseOffset = Base.getBaseOffset() + Layout.getBaseClassOffset(BaseDecl); @@ -91,10 +87,7 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, return; for (const auto &I : RD->bases()) { - const auto *BaseDecl = - cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *BaseDecl = I.getType()->castAsCXXRecordDecl(); // Itanium C++ ABI 2.6.2: // Secondary virtual pointers are present for all bases with either @@ -157,10 +150,7 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, void VTTBuilder::LayoutVirtualVTTs(const CXXRecordDecl *RD, VisitedVirtualBasesSetTy &VBases) { for (const auto &I : RD->bases()) { - const auto *BaseDecl = - cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *BaseDecl = I.getType()->castAsCXXRecordDecl(); // Check if this is a virtual base. if (I.isVirtual()) { |
