llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Egor Zhdan (egorzhdan) <details> <summary>Changes</summary> This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes This is the largest chunk of the API Notes functionality in the upstreaming process. I will soon submit a follow-up patch to actually enable usage of this functionality by having a Clang driver flag that enables API Notes, along with tests. (it might be easier to review commit-by-commit) --- Patch is 74.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/73017.diff 19 Files Affected: - (modified) clang/include/clang/Basic/Attr.td (+45-1) - (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+3) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+7) - (modified) clang/include/clang/Lex/Lexer.h (+1-1) - (modified) clang/include/clang/Parse/Parser.h (+12) - (modified) clang/include/clang/Sema/Sema.h (+36) - (modified) clang/lib/Parse/ParseDecl.cpp (+65) - (modified) clang/lib/Parse/Parser.cpp (+5) - (modified) clang/lib/Sema/CMakeLists.txt (+1) - (added) clang/lib/Sema/SemaAPINotes.cpp (+1014) - (modified) clang/lib/Sema/SemaDecl.cpp (+35) - (modified) clang/lib/Sema/SemaDeclAttr.cpp (+23-14) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+5-1) - (modified) clang/lib/Sema/SemaDeclObjC.cpp (+4) - (modified) clang/lib/Sema/SemaObjCProperty.cpp (+5) - (modified) clang/lib/Sema/SemaTemplate.cpp (+7) - (modified) clang/lib/Sema/SemaType.cpp (+109-72) - (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+2) - (modified) clang/utils/TableGen/ClangAttrEmitter.cpp (+25-1) ``````````diff diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index c2fbdfc66c540d6..acfb75a3dee3e7a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -301,6 +301,9 @@ class VariadicEnumArgument<string name, string type, list<string> values, bit IsExternalType = isExternalType; } +// Represents an attribute wrapped by another attribute. +class AttrArgument<string name, bit opt = 0> : Argument<name, opt>; + // This handles one spelling of an attribute. class Spelling<string name, string variety, int version = 1> { string Name = name; @@ -2257,7 +2260,7 @@ def ObjCBridgeRelated : InheritableAttr { def NSErrorDomain : InheritableAttr { let Spellings = [GNU<"ns_error_domain">]; let Subjects = SubjectList<[Enum], ErrorDiag>; - let Args = [DeclArgument<Var, "ErrorDomain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; let Documentation = [NSErrorDomainDocs]; } @@ -2593,6 +2596,22 @@ def SwiftError : InheritableAttr { let Documentation = [SwiftErrorDocs]; } +def SwiftImportAsNonGeneric : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def SwiftImportPropertyAsAccessors : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + def SwiftName : InheritableAttr { let Spellings = [GNU<"swift_name">]; let Args = [StringArgument<"Name">]; @@ -2614,6 +2633,31 @@ def SwiftPrivate : InheritableAttr { let SimpleHandler = 1; } +def SwiftVersioned : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">, + BoolArgument<"IsReplacedByActive">]; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def SwiftVersionedRemoval : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">, + BoolArgument<"IsReplacedByActive">]; + let SemaHandler = 0; + let Documentation = [InternalOnly]; + let AdditionalMembers = [{ + attr::Kind getAttrKindToRemove() const { + return static_cast<attr::Kind>(getRawKind()); + } + }]; +} + def NoDeref : TypeAttr { let Spellings = [Clang<"noderef">]; let Documentation = [NoDerefDocs]; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index c3753ca2828e25e..02c5b9527f5d5f3 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1606,6 +1606,9 @@ def err_pragma_invalid_keyword : Error< def err_pragma_pipeline_invalid_keyword : Error< "invalid argument; expected 'disable'">; +// API notes. +def err_type_unparsed : Error<"unparsed tokens following type">; + // Pragma unroll support. def warn_pragma_unroll_cuda_value_in_parens : Warning< "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 990692c06d7d3a8..f09f2899754690d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10667,6 +10667,13 @@ def warn_imp_cast_drops_unaligned : Warning< } // end of sema category +let CategoryName = "API Notes Issue" in { + +def err_incompatible_replacement_type : Error< + "API notes replacement type %0 has a different size from original type %1">; + +} // end of API Notes category + let CategoryName = "OpenMP Issue" in { // OpenMP support. def err_omp_expected_var_arg : Error< diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 899e665e7454652..b6ecc7e5ded9e2a 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -198,11 +198,11 @@ class Lexer : public PreprocessorLexer { /// from. Currently this is only used by _Pragma handling. SourceLocation getFileLoc() const { return FileLoc; } -private: /// Lex - Return the next token in the file. If this is the end of file, it /// return the tok::eof token. This implicitly involves the preprocessor. bool Lex(Token &Result); +private: /// Called when the preprocessor is in 'dependency scanning lexing mode'. bool LexDependencyDirectiveToken(Token &Result); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 465453826c0b982..034a8f90abbfaf7 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3609,6 +3609,18 @@ class Parser : public CodeCompletionHandler { ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd); + /// Parse the given string as a type. + /// + /// This is a dangerous utility function currently employed only by API notes. + /// It is not a general entry-point for safely parsing types from strings. + /// + /// \param TypeStr The string to be parsed as a type. + /// \param Context The name of the context in which this string is being + /// parsed, which will be used in diagnostics. + /// \param IncludeLoc The location at which this parse was triggered. + TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context, + SourceLocation IncludeLoc); + //===--------------------------------------------------------------------===// // Modules DeclGroupPtrTy ParseModuleDecl(Sema::ModuleImportState &ImportState); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 59806bcbcbb2dbc..0b300df3ef5b666 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -955,6 +955,10 @@ class Sema final { OpaqueParser = P; } + /// Callback to the parser to parse a type expressed as a string. + std::function<TypeResult(StringRef, StringRef, SourceLocation)> + ParseTypeFromStringCallback; + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -3017,6 +3021,9 @@ class Sema final { ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, QualType T); + QualType AdjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation NameLoc, + TypeSourceInfo *TSInfo); ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, @@ -4742,6 +4749,12 @@ class Sema final { bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, bool SkipArgCountCheck = false); + /// Map any API notes provided for this declaration to attributes on the + /// declaration. + /// + /// Triggered by declaration-attribute processing. + void ProcessAPINotes(Decl *D); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference @@ -4802,6 +4815,29 @@ class Sema final { /// Valid types should not have multiple attributes with different CCs. const AttributedType *getCallingConvAttributedType(QualType T) const; + /// Check whether a nullability type specifier can be added to the given + /// type through some means not written in source (e.g. API notes). + /// + /// \param Type The type to which the nullability specifier will be + /// added. On success, this type will be updated appropriately. + /// + /// \param Nullability The nullability specifier to add. + /// + /// \param DiagLoc The location to use for diagnostics. + /// + /// \param AllowArrayTypes Whether to accept nullability specifiers on an + /// array type (e.g., because it will decay to a pointer). + /// + /// \param OverrideExisting Whether to override an existing, locally-specified + /// nullability specifier rather than complaining about the conflict. + /// + /// \returns true if nullability cannot be applied, false otherwise. + bool CheckImplicitNullabilityTypeSpecifier(QualType &Type, + NullabilityKind Nullability, + SourceLocation DiagLoc, + bool AllowArrayTypes, + bool OverrideExisting); + /// Process the attributes before creating an attributed statement. Returns /// the semantic attributes that have been processed. void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8cb5b09fd3b0fa6..421fec3bed58534 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -8028,6 +8028,71 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc, return false; } +TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context, + SourceLocation IncludeLoc) { + // Consume (unexpanded) tokens up to the end-of-directive. + SmallVector<Token, 4> Tokens; + { + // Create a new buffer from which we will parse the type. + auto &SourceMgr = PP.getSourceManager(); + FileID FID = SourceMgr.createFileID( + llvm::MemoryBuffer::getMemBufferCopy(TypeStr, Context), SrcMgr::C_User, + 0, 0, IncludeLoc); + + // Form a new lexer that references the buffer. + Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP); + L.setParsingPreprocessorDirective(true); + + // Lex the tokens from that buffer. + Token Tok; + do { + L.Lex(Tok); + Tokens.push_back(Tok); + } while (Tok.isNot(tok::eod)); + } + + // Replace the "eod" token with an "eof" token identifying the end of + // the provided string. + Token &EndToken = Tokens.back(); + EndToken.startToken(); + EndToken.setKind(tok::eof); + EndToken.setLocation(Tok.getLocation()); + EndToken.setEofData(TypeStr.data()); + + // Add the current token back. + Tokens.push_back(Tok); + + // Enter the tokens into the token stream. + PP.EnterTokenStream(Tokens, /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); + + // Consume the current token so that we'll start parsing the tokens we + // added to the stream. + ConsumeAnyToken(); + + // Enter a new scope. + ParseScope LocalScope(this, 0); + + // Parse the type. + TypeResult Result = ParseTypeName(nullptr); + + // Check if we parsed the whole thing. + if (Result.isUsable() && + (Tok.isNot(tok::eof) || Tok.getEofData() != TypeStr.data())) { + Diag(Tok.getLocation(), diag::err_type_unparsed); + } + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the 'end of directive' token. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the end token. + if (Tok.is(tok::eof) && Tok.getEofData() == TypeStr.data()) + ConsumeAnyToken(); + return Result; +} + void Parser::DiagnoseBitIntUse(const Token &Tok) { // If the token is for _ExtInt, diagnose it as being deprecated. Otherwise, // the token is about _BitInt and gets (potentially) diagnosed as use of an diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 1baeb2aeb021faa..97bcbb605dcbd75 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -70,6 +70,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) PP.addCommentHandler(CommentSemaHandler.get()); PP.setCodeCompletionHandler(*this); + + Actions.ParseTypeFromStringCallback = + [this](StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc) { + return this->ParseTypeFromString(TypeStr, Context, IncludeLoc); + }; } DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1856a88e9a3271a..4f72bce98fbbec3 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaAvailability.cpp SemaCXXScopeSpec.cpp SemaCast.cpp diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp new file mode 100644 index 000000000000000..4f2ac1acfc01a9d --- /dev/null +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -0,0 +1,1014 @@ +//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the mapping from API notes to declaration attributes. +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesReader.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaInternal.h" + +using namespace clang; + +namespace { +enum IsActive_t : bool { IsNotActive, IsActive }; +enum IsReplacement_t : bool { IsNotReplacement, IsReplacement }; + +struct VersionedInfoMetadata { + /// An empty version refers to unversioned metadata. + VersionTuple Version; + unsigned IsActive : 1; + unsigned IsReplacement : 1; + + VersionedInfoMetadata(VersionTuple Version, IsActive_t Active, + IsReplacement_t Replacement) + : Version(Version), IsActive(Active == IsActive_t::IsActive), + IsReplacement(Replacement == IsReplacement_t::IsReplacement) {} +}; +} // end anonymous namespace + +/// Determine whether this is a multi-level pointer type. +static bool isMultiLevelPointerType(QualType Type) { + QualType Pointee = Type->getPointeeType(); + if (Pointee.isNull()) + return false; + + return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() || + Pointee->isMemberPointerType(); +} + +/// Apply nullability to the given declaration. +static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability, + VersionedInfoMetadata Metadata) { + if (!Metadata.IsActive) + return; + + QualType Type; + + // Nullability for a function/method appertains to the retain type. + if (auto Function = dyn_cast<FunctionDecl>(D)) + Type = Function->getReturnType(); + else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) + Type = Method->getReturnType(); + else if (auto Value = dyn_cast<ValueDecl>(D)) + Type = Value->getType(); + else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) + Type = Property->getType(); + else + return; + + // Check the nullability specifier on this type. + QualType OrigType = Type; + S.CheckImplicitNullabilityTypeSpecifier(Type, Nullability, D->getLocation(), + isa<ParmVarDecl>(D), + /*overrideExisting=*/true); + if (Type.getTypePtr() == OrigType.getTypePtr()) + return; + + if (auto Function = dyn_cast<FunctionDecl>(D)) { + const FunctionType *FnType = Function->getType()->castAs<FunctionType>(); + if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FnType)) + Function->setType(S.Context.getFunctionType(Type, Proto->getParamTypes(), + Proto->getExtProtoInfo())); + else + Function->setType( + S.Context.getFunctionNoProtoType(Type, FnType->getExtInfo())); + } else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { + Method->setReturnType(Type); + + // Make it a context-sensitive keyword if we can. + if (!isMultiLevelPointerType(Type)) + Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier( + Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); + + } else if (auto Value = dyn_cast<ValueDecl>(D)) { + Value->setType(Type); + + // Make it a context-sensitive keyword if we can. + if (auto Parm = dyn_cast<ParmVarDecl>(D)) { + if (Parm->isObjCMethodParameter() && !isMultiLevelPointerType(Type)) + Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier( + Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); + } + } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + Property->setType(Type, Property->getTypeSourceInfo()); + + // Make it a property attribute if we can. + if (!isMultiLevelPointerType(Type)) + Property->setPropertyAttributes( + ObjCPropertyAttribute::kind_null_resettable); + + } else + llvm_unreachable("cannot handle nullability here"); +} + +/// Copy a string into ASTContext-allocated memory. +static StringRef CopyString(ASTContext &Ctx, StringRef String) { + void *mem = Ctx.Allocate(String.size(), alignof(char)); + memcpy(mem, String.data(), String.size()); + return StringRef(static_cast<char *>(mem), String.size()); +} + +static AttributeCommonInfo getDummyAttrInfo() { + return AttributeCommonInfo(SourceRange(), + AttributeCommonInfo::UnknownAttribute, + {AttributeCommonInfo::AS_GNU, + /*Spelling*/ 0, /*IsAlignas*/ false, + /*IsRegularKeywordAttribute*/ false}); +} + +namespace { +template <typename A> struct AttrKindFor {}; + +#define ATTR(X) \ + template <> struct AttrKindFor<X##Attr> { \ + static const attr::Kind value = attr::X; \ + }; +#include "clang/Basic/AttrList.inc" + +/// Handle an attribute introduced by API notes. +/// +/// \param ShouldAddAttribute Whether we should add a new attribute +/// (otherwise, we might remove an existing attribute). +/// \param CreateAttr Create the new attribute to be added. +template <typename A> +void handleAPINotedAttribute( + Sema &S, Decl *D, bool ShouldAddAttribute, VersionedInfoMetadata Metadata, + llvm::function_ref<A *()> CreateAttr, + llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) { + if (Metadata.IsActive) { + auto Existing = GetExistingAttr(D); + if (Existing != D->attr_end()) { + // Remove the existing attribute, and treat it as a superseded + // non-versioned attribute. + auto *Versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true); + + D->getAttrs().erase(Existing); + D->addAttr(Versioned); + } + + // If we're supposed to add a new attribute, do so. + if (ShouldAddAttribute) { + if (auto Attr = CreateAttr()) + D->addAttr(Attr); + } + + } else { + if (ShouldAddAttribute) { + if (auto Attr = CreateAttr()) { + auto *Versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, Metadata.Version, Attr, + /*IsReplacedByActive*/ Metadata.IsReplacement); + D->addAttr(Versioned); + } + } else { + // FIXME: This isn't preserving enough information for things like + // availability, where we're trying to remove a /specific/ kind of + // attribute. + auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit( + S.Context, Metadata.Version, AttrKindFor<A>::value, + /*IsReplacedByActive*/ Metadata.IsReplacement); + D->addAttr(Versioned); + } + } +} + +template <typename A> +void handleAPINotedAttribute(Sema &S, Decl *D, bool ShouldAddAttribute, + VersionedInfoMeta... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/73017 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits