llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Yeoul Na (rapidsna) <details> <summary>Changes</summary> Split from #<!-- -->179612 alongside #<!-- -->204125 (LateParsedAttrType AST node). Adds the resolver logic that walks a `FieldDecl`'s type, finds any `LateParsedAttrType` placeholder inside it, parses the cached attribute tokens, and replaces the placeholder with a concrete `CountAttributedType`. Purely NFC: nothing on this commit creates a `LateParsedAttrType` placeholder in the AST, so the resolver never fires — the producer (parser dispatch + `ActOnLateParsedTypeAttr` wire-up) lands in follow-up commits. Sold as an NFC to keep the review of the mechanism halved. Follow-up PR will add the end-to-end wire-up that populates `LateParsedAttrType` placeholders so this resolver can start firing. --- Full diff: https://github.com/llvm/llvm-project/pull/212906.diff 5 Files Affected: - (modified) clang/include/clang/AST/DependenceFlags.h (+7-2) - (modified) clang/include/clang/AST/TypeBase.h (+9-1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) - (modified) clang/include/clang/Sema/Sema.h (+40) - (modified) clang/lib/Sema/SemaDecl.cpp (+201-1) ``````````diff diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h index c4395259f0758..0a5dd53f7696a 100644 --- a/clang/include/clang/AST/DependenceFlags.h +++ b/clang/include/clang/AST/DependenceFlags.h @@ -69,12 +69,17 @@ struct TypeDependenceScope { /// yields an error type. Error = 16, + /// Whether this type contains a placeholder for a type attribute + /// awaiting late parsing, e.g. `int *__counted_by(count)` where + /// `count` is a later field of the same struct. + LateParsedAttr = 32, + None = 0, - All = 31, + All = 63, DependentInstantiation = Dependent | Instantiation, - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Error) + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/LateParsedAttr) }; }; using TypeDependence = TypeDependenceScope::TypeDependence; diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 530bfe72dac2b..1dbcadbfde574 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2866,6 +2866,13 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { return getDependence() & TypeDependence::VariablyModified; } + /// Whether this type contains a placeholder for a type attribute awaiting + /// late parsing, e.g. `int *__counted_by(count)` where `count` is a later + /// field of the same struct. + bool hasLateParsedAttr() const { + return getDependence() & TypeDependence::LateParsedAttr; + } + /// Whether this type involves a variable-length array type /// with a definite size. bool hasSizedVLAType() const; @@ -3565,7 +3572,8 @@ class LateParsedAttrType : public Type { LateParsedAttrType(QualType Wrapped, QualType Canon, LateParsedTypeAttribute *Attr) - : Type(LateParsedAttr, Canon, Wrapped->getDependence()), + : Type(LateParsedAttr, Canon, + TypeDependence::LateParsedAttr | Wrapped->getDependence()), WrappedTy(Wrapped), LateParsedTypeAttr(Attr) {} public: diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 01527e87c903f..db118c699ce02 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7245,6 +7245,9 @@ def err_builtin_counted_by_ref_invalid_use : Error< "value returned by '__builtin_counted_by_ref' cannot be used in " "%select{an array subscript|a binary}0 expression">; +def err_counted_by_on_nested_pointer : Error< + "'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' attribute on nested pointer type is not allowed">; + let CategoryName = "ARC Semantic Issue" in { // ARC-mode diagnostics. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 778c1a2f5c427..d8081e0095d09 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -142,6 +142,8 @@ class InitializationKind; class InitializationSequence; class InitializedEntity; enum class LangAS : unsigned int; +struct LateParsedAttribute; +struct LateParsedTypeAttribute; class LocalInstantiationScope; class LookupResult; class MangleNumberingContext; @@ -1360,6 +1362,11 @@ class Sema final : public SemaBase { OpaqueParser = P; } + /// Callback type to parse and consume a LateParsedTypeAttribute. Used as an + /// argument to ProcessLateParsedTypeAttributes. + typedef void ParseLateParsedTypeAttributeCB(LateParsedTypeAttribute *LTA, + ParsedAttributes *OutAttrs); + /// Callback to the parser to parse a type expressed as a string. std::function<TypeResult(StringRef, StringRef, SourceLocation)> ParseTypeFromStringCallback; @@ -4428,6 +4435,39 @@ class Sema final : public SemaBase { ArrayRef<Decl *> Fields, SourceLocation LBrac, SourceLocation RBrac, const ParsedAttributesView &AttrList); + /// Resolve placeholders left in field types by late-parsed type + /// attributes, now that \p EnclosingDecl's fields are all visible and + /// name lookup for the attribute's argument can succeed. + /// + /// Called for each record whose scope makes a previously-deferred + /// argument resolvable. That is usually the same record that carries + /// the annotated field, e.g. + /// \code + /// struct Flat { + /// char *__counted_by(len) p; + /// unsigned len; // resolved when Flat finishes parsing + /// }; + /// \endcode + /// but the resolving record can also be an inner or outer record when + /// the count and the annotated field are separated by nested records: + /// \code + /// struct NestedNamed { + /// struct { + /// char *__counted_by(len) p; + /// unsigned len; // resolved when the nested struct finishes, + /// } inner; // since name lookup is closed at that scope + /// }; + /// + /// struct NestedAnon { + /// struct { + /// char *__counted_by(len) p; // len isn't in scope until + /// }; // the anonymous struct is + /// unsigned len; // flattened into NestedAnon, + /// }; // so resolved when it finishes + /// \endcode + void ProcessLateParsedTypeAttributes(RecordDecl *EnclosingDecl, + ParseLateParsedTypeAttributeCB *ParseCB); + /// ActOnTagStartDefinition - Invoked when we have entered the /// scope of a tag's definition (e.g., for an enumeration, class, /// struct, or union). diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a553c6994f0ed..67ce1f1631cdd 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -17240,7 +17241,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, // Always attach attributes to the underlying decl. if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) D = TD->getTemplatedDecl(); - ProcessDeclAttributeList(S, D, Attrs); + ProcessDeclAttributeList(S, D, Attrs, ProcessDeclAttributeOptions()); ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D)) @@ -19983,6 +19984,205 @@ bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) { return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl); } +static QualType handleCountedByAttrField(Sema &S, QualType T, Decl *D, + const ParsedAttr &AL) { + if (!AL.diagnoseLangOpts(S)) + return QualType(); + + assert(isa<FieldDecl>(D)); + + auto *CountExpr = AL.getArgAsExpr(0); + if (!CountExpr) + return QualType(); + + bool CountInBytes; + bool OrNull; + switch (AL.getKind()) { + case ParsedAttr::AT_CountedBy: + CountInBytes = false; + OrNull = false; + break; + case ParsedAttr::AT_CountedByOrNull: + CountInBytes = false; + OrNull = true; + break; + case ParsedAttr::AT_SizedBy: + CountInBytes = true; + OrNull = false; + break; + case ParsedAttr::AT_SizedByOrNull: + CountInBytes = true; + OrNull = true; + break; + default: + llvm_unreachable("unexpected counted_by family attribute"); + } + + return S.BuildCountAttributedArrayOrPointerType(T, CountExpr, CountInBytes, + OrNull); +} +struct RebuildTypeWithLateParsedAttr + : TreeTransform<RebuildTypeWithLateParsedAttr> { + FieldDecl *FD; + Sema::ParseLateParsedTypeAttributeCB *ParseCallback; + + /// Non-zero while transforming under a pointer or array. Used to reject + /// nested counted_by (e.g. `int *__counted_by(n) *p` or + /// `int *__counted_by(n) arr[10]`), where the resolved CountAttributedType + /// would sit inside another wrapper. + unsigned PointerOrArrayDepth = 0; + + RebuildTypeWithLateParsedAttr(Sema &SemaRef, FieldDecl *FD, + Sema::ParseLateParsedTypeAttributeCB *ParseCB) + : TreeTransform(SemaRef), FD(FD), ParseCallback(ParseCB) {} + + QualType TransformLateParsedAttrType(TypeLocBuilder &TLB, + LateParsedAttrTypeLoc TL) { + const LateParsedAttrType *LPA = TL.getTypePtr(); + auto *LTA = LPA->getLateParsedAttribute(); + + assert(LTA && "LateParsedAttrType must have a LateParsedTypeAttribute"); + + AttributeFactory AF{}; + ParsedAttributes Attrs(AF); + + // Invoke the parser callback to parse and consume the cached tokens. + // ParseCallback is also responsible for deleting LTA. + assert(ParseCallback); + ParseCallback(LTA, &Attrs); + + // Invalid argument + if (Attrs.empty()) + return QualType(); + + assert(Attrs.size() == 1); + auto &AL = Attrs[0]; + + QualType InnerType = TransformType(TLB, TL.getInnerLoc()); + if (InnerType.isNull()) { + FD->setInvalidDecl(); + return QualType(); + } + + QualType T = handleCountedByAttrField(SemaRef, InnerType, FD, AL); + if (T.isNull()) { + AL.setInvalid(); + FD->setInvalidDecl(); + return QualType(); + } + + // Reject nested counted_by, e.g. `int *__counted_by(n) *p` or + // `int *__counted_by(n) arr[10]` — the resolved CountAttributedType + // must sit at the outermost level of the field's type. + // + // FIXME: In the -fbounds-safety model: + // - `counted_by` / `sized_by` on nested pointers is permitted only + // for function parameters, and only one level of nesting. + // - `counted_by_or_null` / `sized_by_or_null` on nested pointers is + // generally permitted, because it makes sense to allow such a + // pointer to be valid when the top-level pointer is null (the + // inner counted_by / sized_by simply doesn't need to hold in + // that case). + // We reject unconditionally here because this slice only covers + // struct fields; extending coverage will need to relax this check + // along those lines. + if (PointerOrArrayDepth > 0) { + SemaRef.Diag(TL.getAttrNameLoc(), diag::err_counted_by_on_nested_pointer) + << T->getAs<CountAttributedType>()->getKind(); + FD->setInvalidDecl(); + return QualType(); + } + + AL.setUsedAsTypeAttr(); + + TLB.push<CountAttributedTypeLoc>(T); + return T; + } + + // The five overrides below only track pointer/array depth so + // TransformLateParsedAttrType can reject nested counted_by. Recursion, + // rebuild, and TypeLoc pushing are delegated to the base TreeTransform. + + QualType TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL) { + ++PointerOrArrayDepth; + QualType Result = TreeTransform::TransformPointerType(TLB, TL); + --PointerOrArrayDepth; + if (Result.isNull()) + FD->setInvalidDecl(); + return Result; + } + + QualType TransformConstantArrayType(TypeLocBuilder &TLB, + ConstantArrayTypeLoc TL) { + ++PointerOrArrayDepth; + QualType Result = TreeTransform::TransformConstantArrayType(TLB, TL); + --PointerOrArrayDepth; + if (Result.isNull()) + FD->setInvalidDecl(); + return Result; + } + + QualType TransformIncompleteArrayType(TypeLocBuilder &TLB, + IncompleteArrayTypeLoc TL) { + ++PointerOrArrayDepth; + QualType Result = TreeTransform::TransformIncompleteArrayType(TLB, TL); + --PointerOrArrayDepth; + if (Result.isNull()) + FD->setInvalidDecl(); + return Result; + } + + QualType TransformVariableArrayType(TypeLocBuilder &TLB, + VariableArrayTypeLoc TL) { + ++PointerOrArrayDepth; + QualType Result = TreeTransform::TransformVariableArrayType(TLB, TL); + --PointerOrArrayDepth; + if (Result.isNull()) + FD->setInvalidDecl(); + return Result; + } + + QualType TransformDependentSizedArrayType(TypeLocBuilder &TLB, + DependentSizedArrayTypeLoc TL) { + ++PointerOrArrayDepth; + QualType Result = TreeTransform::TransformDependentSizedArrayType(TLB, TL); + --PointerOrArrayDepth; + if (Result.isNull()) + FD->setInvalidDecl(); + return Result; + } +}; + +void Sema::ProcessLateParsedTypeAttributes( + RecordDecl *EnclosingDecl, ParseLateParsedTypeAttributeCB *ParseCB) { + for (auto *I : EnclosingDecl->decls()) { + FieldDecl *FD = dyn_cast<FieldDecl>(I); + IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(I); + if (!FD && IFD) { + FD = IFD->getAnonField(); + } + if (!FD || !FD->getType()->hasLateParsedAttr() || + FD->getType()->isRecordType()) + continue; + + RebuildTypeWithLateParsedAttr RebuildFieldType(*this, FD, ParseCB); + auto *OldTSI = FD->getTypeSourceInfo(); + auto *TSI = RebuildFieldType.TransformType(FD->getTypeSourceInfo()); + if (TSI && TSI != OldTSI) { + FD->setTypeSourceInfo(TSI); + FD->setType(TSI->getType()); + if (IFD) { + IFD->setType(TSI->getType()); + } + } + + if (auto *CAT = FD->getType()->getAs<CountAttributedType>()) { + CheckCountedByAttrOnField(FD, CAT->getCountExpr(), CAT->isCountInBytes(), + CAT->isOrNull()); + } + } +} + void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, ArrayRef<Decl *> Fields, SourceLocation LBrac, SourceLocation RBrac, `````````` </details> https://github.com/llvm/llvm-project/pull/212906 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
